mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: language specific dropdowns in codegen (#16452)
This commit is contained in:
parent
bd06d1604f
commit
13596b7be3
@ -251,14 +251,14 @@ export class Recorder implements InstrumentationListener {
|
||||
const { file, line } = metadata.stack[0];
|
||||
let source = this._userSources.get(file);
|
||||
if (!source) {
|
||||
source = { isRecorded: false, file, text: this._readSource(file), highlight: [], language: languageForFile(file) };
|
||||
source = { isRecorded: false, label: file, id: file, text: this._readSource(file), highlight: [], language: languageForFile(file) };
|
||||
this._userSources.set(file, source);
|
||||
}
|
||||
if (line) {
|
||||
const paused = this._debugger.isPaused(metadata);
|
||||
source.highlight.push({ line, type: metadata.error ? 'error' : (paused ? 'paused' : 'running') });
|
||||
source.revealLine = line;
|
||||
fileToSelect = source.file;
|
||||
fileToSelect = source.id;
|
||||
}
|
||||
}
|
||||
this._pushAllSources();
|
||||
@ -333,7 +333,9 @@ class ContextRecorder extends EventEmitter {
|
||||
for (const languageGenerator of this._orderedLanguages) {
|
||||
const source: Source = {
|
||||
isRecorded: true,
|
||||
file: languageGenerator.fileName,
|
||||
label: languageGenerator.name,
|
||||
group: languageGenerator.groupName,
|
||||
id: languageGenerator.id,
|
||||
text: generator.generateText(languageGenerator),
|
||||
language: languageGenerator.highlighter,
|
||||
highlight: []
|
||||
@ -345,7 +347,7 @@ class ContextRecorder extends EventEmitter {
|
||||
}
|
||||
this.emit(ContextRecorder.Events.Change, {
|
||||
sources: this._recorderSources,
|
||||
primaryFileName: this._orderedLanguages[0].fileName
|
||||
primaryFileName: this._orderedLanguages[0].id
|
||||
});
|
||||
});
|
||||
context.on(BrowserContext.Events.BeforeClose, () => {
|
||||
@ -362,9 +364,9 @@ class ContextRecorder extends EventEmitter {
|
||||
new JavaLanguageGenerator(),
|
||||
new JavaScriptLanguageGenerator(false),
|
||||
new JavaScriptLanguageGenerator(true),
|
||||
new PythonLanguageGenerator(false, true),
|
||||
new PythonLanguageGenerator(false, false),
|
||||
new PythonLanguageGenerator(true, false),
|
||||
new PythonLanguageGenerator(false, true),
|
||||
new CSharpLanguageGenerator(),
|
||||
]);
|
||||
const primaryLanguage = [...languages].find(l => l.id === language);
|
||||
|
||||
@ -27,7 +27,8 @@ import deviceDescriptors from '../deviceDescriptors';
|
||||
|
||||
export class CSharpLanguageGenerator implements LanguageGenerator {
|
||||
id = 'csharp';
|
||||
fileName = 'C#';
|
||||
groupName = '.NET';
|
||||
name = 'Library C#';
|
||||
highlighter = 'csharp';
|
||||
|
||||
generateAction(actionInContext: ActionInContext): string {
|
||||
|
||||
@ -28,7 +28,8 @@ import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
|
||||
|
||||
export class JavaLanguageGenerator implements LanguageGenerator {
|
||||
id = 'java';
|
||||
fileName = 'Java';
|
||||
groupName = 'Java';
|
||||
name = 'Library';
|
||||
highlighter = 'java';
|
||||
|
||||
generateAction(actionInContext: ActionInContext): string {
|
||||
|
||||
@ -27,13 +27,14 @@ import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
|
||||
|
||||
export class JavaScriptLanguageGenerator implements LanguageGenerator {
|
||||
id: string;
|
||||
fileName: string;
|
||||
groupName = 'Node.js';
|
||||
name: string;
|
||||
highlighter = 'javascript';
|
||||
private _isTest: boolean;
|
||||
|
||||
constructor(isTest: boolean) {
|
||||
this.id = isTest ? 'test' : 'javascript';
|
||||
this.fileName = isTest ? 'Playwright Test' : 'JavaScript';
|
||||
this.name = isTest ? 'Test Runner' : 'Library';
|
||||
this._isTest = isTest;
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,8 @@ export type LanguageGeneratorOptions = {
|
||||
|
||||
export interface LanguageGenerator {
|
||||
id: string;
|
||||
fileName: string;
|
||||
groupName: string;
|
||||
name: string;
|
||||
highlighter: string;
|
||||
generateHeader(options: LanguageGeneratorOptions): string;
|
||||
generateAction(actionInContext: ActionInContext): string;
|
||||
|
||||
@ -26,8 +26,9 @@ import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
|
||||
import deviceDescriptors from '../deviceDescriptors';
|
||||
|
||||
export class PythonLanguageGenerator implements LanguageGenerator {
|
||||
id = 'python';
|
||||
fileName = 'Python';
|
||||
id: string;
|
||||
groupName = 'Python';
|
||||
name: string;
|
||||
highlighter = 'python';
|
||||
|
||||
private _awaitPrefix: '' | 'await ';
|
||||
@ -37,7 +38,7 @@ export class PythonLanguageGenerator implements LanguageGenerator {
|
||||
|
||||
constructor(isAsync: boolean, isPyTest: boolean) {
|
||||
this.id = isPyTest ? 'pytest' : (isAsync ? 'python-async' : 'python');
|
||||
this.fileName = isPyTest ? 'Pytest' : (isAsync ? 'Python Async' : 'Python');
|
||||
this.name = isPyTest ? 'Pytest' : (isAsync ? 'Library Async' : 'Library');
|
||||
this._isAsync = isAsync;
|
||||
this._isPyTest = isPyTest;
|
||||
this._awaitPrefix = isAsync ? 'await ' : '';
|
||||
|
||||
@ -53,9 +53,12 @@ export type SourceHighlight = {
|
||||
|
||||
export type Source = {
|
||||
isRecorded: boolean;
|
||||
file: string;
|
||||
id: string;
|
||||
label: string;
|
||||
text: string;
|
||||
language: string;
|
||||
highlight: SourceHighlight[];
|
||||
revealLine?: number;
|
||||
// used to group the language generators
|
||||
group?: string;
|
||||
};
|
||||
|
||||
@ -53,22 +53,27 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||
setFocusSelectorInput(!!focus);
|
||||
};
|
||||
|
||||
const [f, setFile] = React.useState<string | undefined>();
|
||||
const file = f || sources[0]?.file;
|
||||
const [fileId, setFileId] = React.useState<string | undefined>();
|
||||
|
||||
const source = sources.find(s => s.file === file) || {
|
||||
React.useEffect(() => {
|
||||
if (!fileId && sources.length > 0)
|
||||
setFileId(sources[0].id);
|
||||
}, [fileId, sources]);
|
||||
|
||||
const source: Source = sources.find(s => s.id === fileId) || {
|
||||
id: 'default',
|
||||
isRecorded: false,
|
||||
text: '',
|
||||
language: 'javascript',
|
||||
file: '',
|
||||
label: '',
|
||||
highlight: []
|
||||
};
|
||||
window.playwrightSetFileIfNeeded = (value: string) => {
|
||||
const newSource = sources.find(s => s.file === value);
|
||||
const newSource = sources.find(s => s.id === value);
|
||||
// Do not forcefully switch between two recorded sources, because
|
||||
// user did explicitly choose one.
|
||||
if (newSource && !newSource.isRecorded || !source.isRecorded)
|
||||
setFile(value);
|
||||
setFileId(value);
|
||||
};
|
||||
|
||||
const messagesEndRef = React.createRef<HTMLDivElement>();
|
||||
@ -125,15 +130,9 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||
}}></ToolbarButton>
|
||||
<div style={{ flex: 'auto' }}></div>
|
||||
<div>Target:</div>
|
||||
<select className='recorder-chooser' hidden={!sources.length} value={file} onChange={event => {
|
||||
setFile(event.target.selectedOptions[0].value);
|
||||
}}>{
|
||||
sources.map(s => {
|
||||
const title = s.file.replace(/.*[/\\]([^/\\]+)/, '$1');
|
||||
return <option key={s.file} value={s.file}>{title}</option>;
|
||||
})
|
||||
}
|
||||
</select>
|
||||
<select className='recorder-chooser' hidden={!sources.length} value={fileId} onChange={event => {
|
||||
setFileId(event.target.selectedOptions[0].value);
|
||||
}}>{renderSourceOptions(sources)}</select>
|
||||
<ToolbarButton icon='clear-all' title='Clear' disabled={!source || !source.text} onClick={() => {
|
||||
window.dispatch({ event: 'clear' });
|
||||
}}></ToolbarButton>
|
||||
@ -159,6 +158,25 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||
</div>;
|
||||
};
|
||||
|
||||
function renderSourceOptions(sources: Source[]): React.ReactNode {
|
||||
const transformTitle = (title: string): string => title.replace(/.*[/\\]([^/\\]+)/, '$1');
|
||||
const renderOption = (source: Source): React.ReactNode => (
|
||||
<option key={source.id} value={source.id}>{transformTitle(source.label)}</option>
|
||||
);
|
||||
|
||||
const hasGroup = sources.some(s => s.group);
|
||||
if (hasGroup) {
|
||||
const groups = new Set(sources.map(s => s.group));
|
||||
return Array.from(groups).map(group => (
|
||||
<optgroup label={group} key={group}>
|
||||
{sources.filter(s => s.group === group).map(source => renderOption(source))}
|
||||
</optgroup>
|
||||
));
|
||||
}
|
||||
|
||||
return sources.map(source => renderOption(source));
|
||||
}
|
||||
|
||||
function copy(text: string) {
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.style.position = 'absolute';
|
||||
|
||||
@ -28,6 +28,17 @@ type CLITestArgs = {
|
||||
runCLI: (args: string[], options?: { noAutoExit?: boolean }) => CLIMock;
|
||||
};
|
||||
|
||||
const codegenLang2Id: Map<string, string> = new Map([
|
||||
['JavaScript', 'javascript'],
|
||||
['Java', 'java'],
|
||||
['Python', 'python'],
|
||||
['Python Async', 'python-async'],
|
||||
['Pytest', 'pytest'],
|
||||
['C#', 'csharp'],
|
||||
['Playwright Test', 'test'],
|
||||
]);
|
||||
const codegenLangId2lang = new Map([...codegenLang2Id.entries()].map(([lang, langId]) => [langId, lang]));
|
||||
|
||||
const playwrightToAutomateInspector = require('../../../packages/playwright-core/lib/inProcessFactory').createInProcessPlaywright();
|
||||
|
||||
export const test = contextTest.extend<CLITestArgs>({
|
||||
@ -115,14 +126,19 @@ class Recorder {
|
||||
}
|
||||
|
||||
async waitForOutput(file: string, text: string): Promise<Map<string, Source>> {
|
||||
const handle = await this.recorderPage.waitForFunction((params: { text: string, file: string }) => {
|
||||
if (!codegenLang2Id.has(file))
|
||||
throw new Error(`Unknown language: ${file}`);
|
||||
const handle = await this.recorderPage.waitForFunction((params: { text: string, languageId: string }) => {
|
||||
const w = window as any;
|
||||
const source = (w.playwrightSourcesEchoForTest || []).find((s: Source) => s.file === params.file);
|
||||
const source = (w.playwrightSourcesEchoForTest || []).find((s: Source) => s.id === params.languageId);
|
||||
return source && source.text.includes(params.text) ? w.playwrightSourcesEchoForTest : null;
|
||||
}, { text, file }, { timeout: 8000, polling: 300 });
|
||||
}, { text, languageId: codegenLang2Id.get(file) }, { timeout: 8000, polling: 300 });
|
||||
const sources: Source[] = await handle.jsonValue();
|
||||
for (const source of sources)
|
||||
this._sources.set(source.file, source);
|
||||
for (const source of sources) {
|
||||
if (!codegenLangId2lang.has(source.id))
|
||||
throw new Error(`Unknown language: ${source.id}`);
|
||||
this._sources.set(codegenLangId2lang.get(source.id), source);
|
||||
}
|
||||
return this._sources;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user