fix(codegen): update csharp scripts to new syntax (#6685)
Drive-by: fix middle/right button clicks in codegen.
This commit is contained in:
parent
08773e836b
commit
e4946b79e6
|
|
@ -142,6 +142,7 @@ export class Recorder {
|
|||
removeEventListeners(this._listeners);
|
||||
this._listeners = [
|
||||
addEventListener(document, 'click', event => this._onClick(event as MouseEvent), true),
|
||||
addEventListener(document, 'auxclick', event => this._onClick(event as MouseEvent), true),
|
||||
addEventListener(document, 'input', event => this._onInput(event), true),
|
||||
addEventListener(document, 'keydown', event => this._onKeyDown(event as KeyboardEvent), true),
|
||||
addEventListener(document, 'keyup', event => this._onKeyUp(event as KeyboardEvent), true),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||
|
||||
generateAction(actionInContext: ActionInContext): string {
|
||||
const { action, pageAlias } = actionInContext;
|
||||
const formatter = new CSharpFormatter(0);
|
||||
const formatter = new CSharpFormatter(8);
|
||||
formatter.newLine();
|
||||
formatter.add('// ' + actionTitle(action));
|
||||
|
||||
|
|
@ -41,63 +41,62 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||
|
||||
const subject = actionInContext.isMainFrame ? pageAlias :
|
||||
(actionInContext.frameName ?
|
||||
`${pageAlias}.GetFrame(name: ${quote(actionInContext.frameName)})` :
|
||||
`${pageAlias}.GetFrame(url: ${quote(actionInContext.frameUrl)})`);
|
||||
`${pageAlias}.Frame(${quote(actionInContext.frameName)})` :
|
||||
`${pageAlias}.FrameByUrl(${quote(actionInContext.frameUrl)})`);
|
||||
|
||||
const signals = toSignalMap(action);
|
||||
|
||||
if (signals.dialog) {
|
||||
formatter.add(` void ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler(object sender, DialogEventArgs e)
|
||||
formatter.add(` void ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler(object sender, IDialog dialog)
|
||||
{
|
||||
Console.WriteLine($"Dialog message: {e.Dialog.Message}");
|
||||
e.Dialog.DismissAsync();
|
||||
Console.WriteLine($"Dialog message: {dialog.Message}");
|
||||
dialog.DismissAsync();
|
||||
${pageAlias}.Dialog -= ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;
|
||||
}
|
||||
${pageAlias}.Dialog += ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;`);
|
||||
}
|
||||
|
||||
const emitTaskWhenAll = signals.waitForNavigation || signals.popup || signals.download;
|
||||
if (emitTaskWhenAll) {
|
||||
if (signals.popup)
|
||||
formatter.add(`var ${signals.popup.popupAlias}Task = ${pageAlias}.WaitForEventAsync(PageEvent.Popup)`);
|
||||
else if (signals.download)
|
||||
formatter.add(`var downloadTask = ${pageAlias}.WaitForEventAsync(PageEvent.Download);`);
|
||||
|
||||
formatter.add(`await Task.WhenAll(`);
|
||||
const lines: string[] = [];
|
||||
const actionCall = this._generateActionCall(action, actionInContext.isMainFrame);
|
||||
if (signals.waitForNavigation) {
|
||||
lines.push(`await Task.WhenAll(`);
|
||||
lines.push(`${pageAlias}.WaitForNavigationAsync(/* new ${actionInContext.isMainFrame ? 'Page' : 'Frame'}WaitForNavigationOptions`);
|
||||
lines.push(`{`);
|
||||
lines.push(` UrlString = ${quote(signals.waitForNavigation.url)}`);
|
||||
lines.push(`} */),`);
|
||||
lines.push(`${subject}.${actionCall});`);
|
||||
} else {
|
||||
lines.push(`await ${subject}.${actionCall};`);
|
||||
}
|
||||
|
||||
// Popup signals.
|
||||
if (signals.popup)
|
||||
formatter.add(`${signals.popup.popupAlias}Task,`);
|
||||
if (signals.download) {
|
||||
lines.unshift(`var download${signals.download.downloadAlias} = await ${pageAlias}.RunAndWaitForEventAsync(PageEvent.Download, async () =>\n{`);
|
||||
lines.push(`});`);
|
||||
}
|
||||
|
||||
// Navigation signal.
|
||||
if (signals.waitForNavigation)
|
||||
formatter.add(`${pageAlias}.WaitForNavigationAsync(/*${quote(signals.waitForNavigation.url)}*/),`);
|
||||
if (signals.popup) {
|
||||
lines.unshift(`var ${signals.popup.popupAlias} = await ${pageAlias}.RunAndWaitForEventAsync(PageEvent.Popup, async () =>\n{`);
|
||||
lines.push(`});`);
|
||||
}
|
||||
|
||||
// Download signals.
|
||||
if (signals.download)
|
||||
formatter.add(`downloadTask,`);
|
||||
|
||||
const prefix = (signals.popup || signals.waitForNavigation || signals.download) ? '' : 'await ';
|
||||
const actionCall = this._generateActionCall(action);
|
||||
const suffix = emitTaskWhenAll ? ');' : ';';
|
||||
formatter.add(`${prefix}${subject}.${actionCall}${suffix}`);
|
||||
for (const line of lines)
|
||||
formatter.add(line);
|
||||
|
||||
if (signals.assertNavigation)
|
||||
formatter.add(` // Assert.Equal(${quote(signals.assertNavigation.url)}, ${pageAlias}.Url);`);
|
||||
return formatter.format();
|
||||
}
|
||||
|
||||
private _generateActionCall(action: Action): string {
|
||||
private _generateActionCall(action: Action, isPage: boolean): string {
|
||||
switch (action.name) {
|
||||
case 'openPage':
|
||||
throw Error('Not reached');
|
||||
case 'closePage':
|
||||
return 'CloseAsync()';
|
||||
case 'click': {
|
||||
let method = 'ClickAsync';
|
||||
let method = 'Click';
|
||||
if (action.clickCount === 2)
|
||||
method = 'DblClickAsync';
|
||||
method = 'DblClick';
|
||||
const modifiers = toModifiers(action.modifiers);
|
||||
const options: MouseClickOptions = {};
|
||||
if (action.button !== 'left')
|
||||
|
|
@ -106,8 +105,10 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||
options.modifiers = modifiers;
|
||||
if (action.clickCount > 2)
|
||||
options.clickCount = action.clickCount;
|
||||
const optionsString = formatOptions(options, true, false);
|
||||
return `${method}(${quote(action.selector)}${optionsString})`;
|
||||
if (!Object.entries(options).length)
|
||||
return `${method}Async(${quote(action.selector)})`;
|
||||
const optionsString = formatObject(options, ' ', (isPage ? 'Page' : 'Frame') + method + 'Options');
|
||||
return `${method}Async(${quote(action.selector)}, ${optionsString})`;
|
||||
}
|
||||
case 'check':
|
||||
return `CheckAsync(${quote(action.selector)})`;
|
||||
|
|
@ -116,7 +117,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||
case 'fill':
|
||||
return `FillAsync(${quote(action.selector)}, ${quote(action.text)})`;
|
||||
case 'setInputFiles':
|
||||
return `SetInputFilesAsync(${quote(action.selector)}, ${formatObject(action.files.length === 1 ? action.files[0] : action.files)})`;
|
||||
return `SetInputFilesAsync(${quote(action.selector)}, ${formatObject(action.files)})`;
|
||||
case 'press': {
|
||||
const modifiers = toModifiers(action.modifiers);
|
||||
const shortcut = [...modifiers, action.key].join('+');
|
||||
|
|
@ -125,68 +126,37 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||
case 'navigate':
|
||||
return `GotoAsync(${quote(action.url)})`;
|
||||
case 'select':
|
||||
return `SelectOptionAsync(${quote(action.selector)}, ${formatObject(action.options.length > 1 ? action.options : action.options[0])})`;
|
||||
return `SelectOptionAsync(${quote(action.selector)}, ${formatObject(action.options)})`;
|
||||
}
|
||||
}
|
||||
|
||||
generateHeader(options: LanguageGeneratorOptions): string {
|
||||
const formatter = new CSharpFormatter(0);
|
||||
formatter.add(`
|
||||
await Playwright.InstallAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatArgs(options.launchOptions)}
|
||||
);
|
||||
var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Playwright;
|
||||
|
||||
class Example
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject(options.launchOptions, ' ', 'BrowserTypeLaunchOptions')});
|
||||
var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);
|
||||
return formatter.format();
|
||||
}
|
||||
|
||||
generateFooter(saveStorage: string | undefined): string {
|
||||
const storageStateLine = saveStorage ? `\nawait context.StorageStateAsync(path: "${saveStorage}");` : '';
|
||||
return `\n// ---------------------${storageStateLine}`;
|
||||
const storageStateLine = saveStorage ? `\n await context.StorageStateAsync(new BrowserContextStorageStateOptions\n {\n Path = ${quote(saveStorage)}\n });\n` : '';
|
||||
return `${storageStateLine} }
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
function formatValue(value: any): string {
|
||||
if (value === false)
|
||||
return 'false';
|
||||
if (value === true)
|
||||
return 'true';
|
||||
if (value === undefined)
|
||||
return 'null';
|
||||
if (Array.isArray(value))
|
||||
return `new [] {${value.map(formatValue).join(', ')}}`;
|
||||
if (typeof value === 'string')
|
||||
return quote(value);
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function formatOptions(value: any, hasArguments: boolean, isInitializing: boolean): string {
|
||||
const keys = Object.keys(value);
|
||||
if (!keys.length)
|
||||
return '';
|
||||
return (hasArguments ? ', ' : '') + keys.map(key => `${key}${isInitializing ? ': ' : ' = '}${formatValue(value[key])}`).join(', ');
|
||||
}
|
||||
|
||||
function formatArgs(value: any, indent = ' '): string {
|
||||
if (typeof value === 'string')
|
||||
return quote(value);
|
||||
if (Array.isArray(value))
|
||||
return `new [] {${value.map(o => formatObject(o)).join(', ')}}`;
|
||||
if (typeof value === 'object') {
|
||||
const keys = Object.keys(value);
|
||||
if (!keys.length)
|
||||
return '';
|
||||
const tokens: string[] = [];
|
||||
for (const key of keys)
|
||||
tokens.push(`${keys.length !== 1 ? indent : ''}${key}: ${formatObject(value[key], indent, key)}`);
|
||||
return `\n${indent}${tokens.join(`,\n${indent}`)}`;
|
||||
}
|
||||
return String(value);
|
||||
}
|
||||
|
||||
function formatObject(value: any, indent = ' ', name = ''): string {
|
||||
if (typeof value === 'string') {
|
||||
if (name === 'permissions' || name === 'colorScheme')
|
||||
if (['permissions', 'colorScheme', 'modifiers', 'button'].includes(name))
|
||||
return `${getClassName(name)}.${toPascal(value)}`;
|
||||
return quote(value);
|
||||
}
|
||||
|
|
@ -195,10 +165,12 @@ function formatObject(value: any, indent = ' ', name = ''): string {
|
|||
if (typeof value === 'object') {
|
||||
const keys = Object.keys(value);
|
||||
if (!keys.length)
|
||||
return '';
|
||||
return name ? `new ${getClassName(name)}` : '';
|
||||
const tokens: string[] = [];
|
||||
for (const key of keys)
|
||||
tokens.push(`${toPascal(key)} = ${formatObject(value[key], indent, key)},`);
|
||||
for (const key of keys) {
|
||||
const property = getPropertyName(key);
|
||||
tokens.push(`${property} = ${formatObject(value[key], indent, key)},`);
|
||||
}
|
||||
if (name)
|
||||
return `new ${getClassName(name)}\n{\n${indent}${tokens.join(`\n${indent}`)}\n${indent}}`;
|
||||
return `{\n${indent}${tokens.join(`\n${indent}`)}\n${indent}}`;
|
||||
|
|
@ -214,31 +186,37 @@ function getClassName(value: string): string {
|
|||
case 'viewport': return 'ViewportSize';
|
||||
case 'proxy': return 'ProxySettings';
|
||||
case 'permissions': return 'ContextPermission';
|
||||
case 'modifiers': return 'KeyboardModifier';
|
||||
case 'button': return 'MouseButton';
|
||||
default: return toPascal(value);
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertyName(key: string): string {
|
||||
switch (key) {
|
||||
case 'storageState': return 'StorageStatePath';
|
||||
case 'viewport': return 'ViewportSize';
|
||||
default: return toPascal(key);
|
||||
}
|
||||
}
|
||||
|
||||
function toPascal(value: string): string {
|
||||
return value[0].toUpperCase() + value.slice(1);
|
||||
}
|
||||
|
||||
function formatContextOptions(options: BrowserContextOptions, deviceName: string | undefined): string {
|
||||
const device = deviceName && deviceDescriptors[deviceName];
|
||||
if (!device)
|
||||
return formatArgs(options);
|
||||
const serializedObject = formatObject(sanitizeDeviceOptions(device, options), ' ');
|
||||
// When there are no additional context options, we still want to spread the device inside.
|
||||
|
||||
if (!serializedObject)
|
||||
return `playwright.Devices["${deviceName}"]`;
|
||||
let result = `new BrowserContextOptions(playwright.Devices["${deviceName}"])`;
|
||||
|
||||
if (serializedObject) {
|
||||
const lines = serializedObject.split('\n');
|
||||
result = `${result} \n${lines.join('\n')}`;
|
||||
if (!device) {
|
||||
if (!Object.entries(options).length)
|
||||
return '';
|
||||
return formatObject(options, ' ', 'BrowserNewContextOptions');
|
||||
}
|
||||
|
||||
return result;
|
||||
options = sanitizeDeviceOptions(device, options);
|
||||
if (!Object.entries(options).length)
|
||||
return `playwright.Devices[${quote(deviceName!)}]`;
|
||||
|
||||
return formatObject(options, ' ', `BrowserNewContextOptions(playwright.Devices[${quote(deviceName!)}])`);
|
||||
}
|
||||
|
||||
class CSharpFormatter {
|
||||
|
|
@ -278,7 +256,7 @@ class CSharpFormatter {
|
|||
line = spaces + extraSpaces + line;
|
||||
if (line.endsWith('{') || line.endsWith('[') || line.endsWith('('))
|
||||
spaces += this._baseIndent;
|
||||
if (line.endsWith('});'))
|
||||
if (line.endsWith('));'))
|
||||
spaces = spaces.substring(this._baseIndent.length);
|
||||
|
||||
return this._baseOffset + line;
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ export type PopupSignal = BaseSignal & {
|
|||
|
||||
export type DownloadSignal = BaseSignal & {
|
||||
name: 'download',
|
||||
downloadAlias: string,
|
||||
};
|
||||
|
||||
export type DialogSignal = BaseSignal & {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export class RecorderSupplement implements InstrumentationListener {
|
|||
private _pageAliases = new Map<Page, string>();
|
||||
private _lastPopupOrdinal = 0;
|
||||
private _lastDialogOrdinal = 0;
|
||||
private _lastDownloadOrdinal = 0;
|
||||
private _timers = new Set<NodeJS.Timeout>();
|
||||
private _context: BrowserContext;
|
||||
private _mode: Mode;
|
||||
|
|
@ -381,7 +382,7 @@ export class RecorderSupplement implements InstrumentationListener {
|
|||
}
|
||||
private _onDownload(page: Page) {
|
||||
const pageAlias = this._pageAliases.get(page)!;
|
||||
this._generator.signal(pageAlias, page.mainFrame(), { name: 'download' });
|
||||
this._generator.signal(pageAlias, page.mainFrame(), { name: 'download', downloadAlias: String(++this._lastDownloadOrdinal) });
|
||||
}
|
||||
|
||||
private _onDialog(page: Page) {
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ test.describe('cli codegen', () => {
|
|||
page.click("text=Submit");`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=Submit
|
||||
await page.ClickAsync("text=Submit");`);
|
||||
// Click text=Submit
|
||||
await page.ClickAsync("text=Submit");`);
|
||||
|
||||
expect(message.text()).toBe('click');
|
||||
});
|
||||
|
|
@ -125,8 +125,8 @@ await page.ClickAsync("text=Submit");`);
|
|||
page.click("text=Submit");`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=Submit
|
||||
await page.ClickAsync("text=Submit");`);
|
||||
// Click text=Submit
|
||||
await page.ClickAsync("text=Submit");`);
|
||||
|
||||
expect(message.text()).toBe('click');
|
||||
});
|
||||
|
|
@ -194,8 +194,8 @@ await page.ClickAsync("text=Submit");`);
|
|||
await page.fill(\"input[name=\\\"name\\\"]\", \"John\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Fill input[name="name"]
|
||||
await page.FillAsync(\"input[name=\\\"name\\\"]\", \"John\");`);
|
||||
// Fill input[name="name"]
|
||||
await page.FillAsync(\"input[name=\\\"name\\\"]\", \"John\");`);
|
||||
|
||||
expect(message.text()).toBe('John');
|
||||
});
|
||||
|
|
@ -251,8 +251,8 @@ await page.FillAsync(\"input[name=\\\"name\\\"]\", \"John\");`);
|
|||
await page.press(\"input[name=\\\"name\\\"]\", \"Shift+Enter\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Press Enter with modifiers
|
||||
await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
||||
// Press Enter with modifiers
|
||||
await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
||||
|
||||
expect(messages[0].text()).toBe('press');
|
||||
});
|
||||
|
|
@ -369,8 +369,8 @@ await page.PressAsync(\"input[name=\\\"name\\\"]\", \"Shift+Enter\");`);
|
|||
await page.check(\"input[name=\\\"accept\\\"]\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Check input[name="accept"]
|
||||
await page.CheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
||||
// Check input[name="accept"]
|
||||
await page.CheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
||||
|
||||
expect(message.text()).toBe('true');
|
||||
});
|
||||
|
|
@ -426,8 +426,8 @@ await page.CheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
|||
await page.uncheck(\"input[name=\\\"accept\\\"]\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Uncheck input[name="accept"]
|
||||
await page.UncheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
||||
// Uncheck input[name="accept"]
|
||||
await page.UncheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
||||
|
||||
expect(message.text()).toBe('false');
|
||||
});
|
||||
|
|
@ -463,8 +463,8 @@ await page.UncheckAsync(\"input[name=\\\"accept\\\"]\");`);
|
|||
await page.select_option(\"select\", \"2\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Select 2
|
||||
await page.SelectOptionAsync(\"select\", \"2\");`);
|
||||
// Select 2
|
||||
await page.SelectOptionAsync(\"select\", new[] { \"2\" });`);
|
||||
|
||||
expect(message.text()).toBe('2');
|
||||
});
|
||||
|
|
@ -510,10 +510,10 @@ await page.SelectOptionAsync(\"select\", \"2\");`);
|
|||
page1 = await popup_info.value`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
var page1Task = page.WaitForEventAsync(PageEvent.Popup)
|
||||
await Task.WhenAll(
|
||||
page1Task,
|
||||
page.ClickAsync(\"text=link\"));`);
|
||||
var page1 = await page.RunAndWaitForEventAsync(PageEvent.Popup, async () =>
|
||||
{
|
||||
await page.ClickAsync(\"text=link\");
|
||||
});`);
|
||||
|
||||
expect(popup.url()).toBe('about:blank');
|
||||
});
|
||||
|
|
@ -552,9 +552,9 @@ await Task.WhenAll(
|
|||
# assert page.url == \"about:blank#foo\"`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=link
|
||||
await page.ClickAsync(\"text=link\");
|
||||
// Assert.Equal(\"about:blank#foo\", page.Url);`);
|
||||
// Click text=link
|
||||
await page.ClickAsync(\"text=link\");
|
||||
// Assert.Equal(\"about:blank#foo\", page.Url);`);
|
||||
|
||||
expect(page.url()).toContain('about:blank#foo');
|
||||
});
|
||||
|
|
@ -601,10 +601,13 @@ await page.ClickAsync(\"text=link\");
|
|||
await page.click(\"text=link\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=link
|
||||
await Task.WhenAll(
|
||||
page.WaitForNavigationAsync(/*\"about:blank#foo\"*/),
|
||||
page.ClickAsync(\"text=link\"));`);
|
||||
// Click text=link
|
||||
await Task.WhenAll(
|
||||
page.WaitForNavigationAsync(/* new PageWaitForNavigationOptions
|
||||
{
|
||||
UrlString = \"about:blank#foo\"
|
||||
} */),
|
||||
page.ClickAsync(\"text=link\"));`);
|
||||
|
||||
expect(page.url()).toContain('about:blank#foo');
|
||||
});
|
||||
|
|
@ -622,4 +625,36 @@ await Task.WhenAll(
|
|||
expect(recorder.sources().get('<javascript>').text).not.toContain(`await page.press('input', 'AltGraph');`);
|
||||
expect(recorder.sources().get('<javascript>').text).toContain(`await page.fill('input', 'playwright@example.com');`);
|
||||
});
|
||||
|
||||
test('should middle click', async ({ page, openRecorder, server }) => {
|
||||
const recorder = await openRecorder();
|
||||
|
||||
await recorder.setContentAndWait(`<a href${JSON.stringify(server.EMPTY_PAGE)}>Click me</a>`);
|
||||
|
||||
const [sources] = await Promise.all([
|
||||
recorder.waitForOutput('<javascript>', 'click'),
|
||||
page.click('a', { button: 'middle' }),
|
||||
]);
|
||||
|
||||
expect(sources.get('<javascript>').text).toContain(`
|
||||
await page.click('text=Click me', {
|
||||
button: 'middle'
|
||||
});`);
|
||||
|
||||
expect(sources.get('<python>').text).toContain(`
|
||||
page.click("text=Click me", button="middle")`);
|
||||
|
||||
expect(sources.get('<async python>').text).toContain(`
|
||||
await page.click("text=Click me", button="middle")`);
|
||||
|
||||
// TODO: fix this for java
|
||||
// expect(sources.get('<java>').text).toContain(`
|
||||
// page.click("text=Click me", foo);`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
await page.ClickAsync("text=Click me", new PageClickOptions
|
||||
{
|
||||
Button = MouseButton.Middle,
|
||||
});`);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ test.describe('cli codegen', () => {
|
|||
page = await context.new_page()`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Open new page
|
||||
var page = await context.NewPageAsync();`);
|
||||
// Open new page
|
||||
var page = await context.NewPageAsync();`);
|
||||
});
|
||||
|
||||
test('should contain second page', async ({ openRecorder, page }) => {
|
||||
|
|
@ -71,8 +71,8 @@ var page = await context.NewPageAsync();`);
|
|||
page1 = await context.new_page()`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Open new page
|
||||
var page1 = await context.NewPageAsync();`);
|
||||
// Open new page
|
||||
var page1 = await context.NewPageAsync();`);
|
||||
});
|
||||
|
||||
test('should contain close page', async ({ openRecorder, page }) => {
|
||||
|
|
@ -96,7 +96,7 @@ var page1 = await context.NewPageAsync();`);
|
|||
await page.close()`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
await page.CloseAsync();`);
|
||||
await page.CloseAsync();`);
|
||||
});
|
||||
|
||||
test('should not lead to an error if html gets clicked', async ({ page, openRecorder }) => {
|
||||
|
|
@ -147,9 +147,8 @@ await page.CloseAsync();`);
|
|||
await page.set_input_files(\"input[type=\\\"file\\\"]\", \"file-to-upload.txt\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Upload file-to-upload.txt
|
||||
await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", \"file-to-upload.txt\");`);
|
||||
|
||||
// Upload file-to-upload.txt
|
||||
await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { \"file-to-upload.txt\" });`);
|
||||
});
|
||||
|
||||
test('should upload multiple files', async ({ page, openRecorder, browserName, asset }) => {
|
||||
|
|
@ -185,8 +184,8 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", \"file-to-upload.txt
|
|||
await page.set_input_files(\"input[type=\\\"file\\\"]\", [\"file-to-upload.txt\", \"file-to-upload-2.txt\"]`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Upload file-to-upload.txt, file-to-upload-2.txt
|
||||
await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { \"file-to-upload.txt\", \"file-to-upload-2.txt\" });`);
|
||||
// Upload file-to-upload.txt, file-to-upload-2.txt
|
||||
await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { \"file-to-upload.txt\", \"file-to-upload-2.txt\" });`);
|
||||
});
|
||||
|
||||
test('should clear files', async ({ page, openRecorder, browserName, asset }) => {
|
||||
|
|
@ -222,8 +221,8 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { \"file-to-up
|
|||
await page.set_input_files(\"input[type=\\\"file\\\"]\", []`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Clear selected files
|
||||
await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { });`);
|
||||
// Clear selected files
|
||||
await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { });`);
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -277,11 +276,11 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { });`);
|
|||
download = await download_info.value`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=Download
|
||||
var downloadTask = page.WaitForEventAsync(PageEvent.Download);
|
||||
await Task.WhenAll(
|
||||
downloadTask,
|
||||
page.ClickAsync(\"text=Download\"));`);
|
||||
// Click text=Download
|
||||
var download1 = await page.RunAndWaitForEventAsync(PageEvent.Download, async () =>
|
||||
{
|
||||
await page.ClickAsync(\"text=Download\");
|
||||
});`);
|
||||
});
|
||||
|
||||
test('should handle dialogs', async ({ page, openRecorder }) => {
|
||||
|
|
@ -325,15 +324,15 @@ await Task.WhenAll(
|
|||
await page.click(\"text=click me\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=click me
|
||||
void page_Dialog1_EventHandler(object sender, DialogEventArgs e)
|
||||
{
|
||||
Console.WriteLine($\"Dialog message: {e.Dialog.Message}\");
|
||||
e.Dialog.DismissAsync();
|
||||
page.Dialog -= page_Dialog1_EventHandler;
|
||||
}
|
||||
page.Dialog += page_Dialog1_EventHandler;
|
||||
await page.ClickAsync(\"text=click me\");`);
|
||||
// Click text=click me
|
||||
void page_Dialog1_EventHandler(object sender, IDialog dialog)
|
||||
{
|
||||
Console.WriteLine($\"Dialog message: {dialog.Message}\");
|
||||
dialog.DismissAsync();
|
||||
page.Dialog -= page_Dialog1_EventHandler;
|
||||
}
|
||||
page.Dialog += page_Dialog1_EventHandler;
|
||||
await page.ClickAsync(\"text=click me\");`);
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -375,9 +374,9 @@ await page.ClickAsync(\"text=click me\");`);
|
|||
page1 = await context.new_page()
|
||||
await page1.goto("about:blank?foo")`);
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Open new page
|
||||
var page1 = await context.NewPageAsync();
|
||||
await page1.GotoAsync("about:blank?foo");`);
|
||||
// Open new page
|
||||
var page1 = await context.NewPageAsync();
|
||||
await page1.GotoAsync("about:blank?foo");`);
|
||||
} else if (browserName === 'firefox') {
|
||||
expect(sources.get('<javascript>').text).toContain(`
|
||||
// Click text=link
|
||||
|
|
@ -521,8 +520,8 @@ await page1.GotoAsync("about:blank?foo");`);
|
|||
await page.frame(name=\"one\").click(\"text=Hi, I'm frame\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=Hi, I'm frame
|
||||
await page.GetFrame(name: \"one\").ClickAsync(\"text=Hi, I'm frame\");`);
|
||||
// Click text=Hi, I'm frame
|
||||
await page.Frame(\"one\").ClickAsync(\"text=Hi, I'm frame\");`);
|
||||
|
||||
[sources] = await Promise.all([
|
||||
recorder.waitForOutput('<javascript>', 'two'),
|
||||
|
|
@ -548,8 +547,8 @@ await page.GetFrame(name: \"one\").ClickAsync(\"text=Hi, I'm frame\");`);
|
|||
await page.frame(name=\"two\").click(\"text=Hi, I'm frame\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=Hi, I'm frame
|
||||
await page.GetFrame(name: \"two\").ClickAsync(\"text=Hi, I'm frame\");`);
|
||||
// Click text=Hi, I'm frame
|
||||
await page.Frame(\"two\").ClickAsync(\"text=Hi, I'm frame\");`);
|
||||
|
||||
[sources] = await Promise.all([
|
||||
recorder.waitForOutput('<javascript>', 'url: \''),
|
||||
|
|
@ -575,8 +574,8 @@ await page.GetFrame(name: \"two\").ClickAsync(\"text=Hi, I'm frame\");`);
|
|||
await page.frame(url=\"http://localhost:${server.PORT}/frames/frame.html\").click(\"text=Hi, I'm frame\")`);
|
||||
|
||||
expect(sources.get('<csharp>').text).toContain(`
|
||||
// Click text=Hi, I'm frame
|
||||
await page.GetFrame(url: \"http://localhost:${server.PORT}/frames/frame.html\").ClickAsync(\"text=Hi, I'm frame\");`);
|
||||
// Click text=Hi, I'm frame
|
||||
await page.FrameByUrl(\"http://localhost:${server.PORT}/frames/frame.html\").ClickAsync(\"text=Hi, I'm frame\");`);
|
||||
});
|
||||
|
||||
test('should record navigations after identical pushState', async ({ page, openRecorder, server }) => {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import { test, expect } from './inspectorTest';
|
|||
|
||||
const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString();
|
||||
const launchOptions = (channel: string) => {
|
||||
return channel ? `headless: false,\n channel: "${channel}"` : 'headless: false';
|
||||
return channel ? `Headless = false,\n Channel = "${channel}",` : `Headless = false,`;
|
||||
};
|
||||
|
||||
function capitalize(browserName: string): string {
|
||||
|
|
@ -29,12 +29,12 @@ function capitalize(browserName: string): string {
|
|||
|
||||
test('should print the correct imports and context options', async ({ browserName, channel, runCLI }) => {
|
||||
const cli = runCLI(['--target=csharp', emptyHTML]);
|
||||
const expectedResult = `await Playwright.InstallAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(
|
||||
${launchOptions(channel)}
|
||||
);
|
||||
var context = await browser.NewContextAsync();`;
|
||||
const expectedResult = ` using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
|
||||
{
|
||||
${launchOptions(channel)}
|
||||
});
|
||||
var context = await browser.NewContextAsync();`;
|
||||
await cli.waitFor(expectedResult).catch(e => e);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
|
@ -50,31 +50,34 @@ test('should print the correct context options for custom settings', async ({ br
|
|||
'--viewport-size=1280,720',
|
||||
'--target=csharp',
|
||||
emptyHTML]);
|
||||
const expectedResult = `await Playwright.InstallAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(
|
||||
${launchOptions(channel)},
|
||||
proxy: new ProxySettings
|
||||
{
|
||||
Server = "http://myproxy:3128",
|
||||
}
|
||||
);
|
||||
var context = await browser.NewContextAsync(
|
||||
viewport: new ViewportSize
|
||||
{
|
||||
Width = 1280,
|
||||
Height = 720,
|
||||
},
|
||||
geolocation: new Geolocation
|
||||
{
|
||||
Latitude = 37.819722m,
|
||||
Longitude = -122.478611m,
|
||||
},
|
||||
permissions: new[] { ContextPermission.Geolocation },
|
||||
userAgent: "hardkodemium",
|
||||
locale: "es",
|
||||
colorScheme: ColorScheme.Dark,
|
||||
timezoneId: "Europe/Rome");`;
|
||||
const expectedResult = `
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
|
||||
{
|
||||
${launchOptions(channel)}
|
||||
Proxy = new ProxySettings
|
||||
{
|
||||
Server = "http://myproxy:3128",
|
||||
},
|
||||
});
|
||||
var context = await browser.NewContextAsync(new BrowserNewContextOptions
|
||||
{
|
||||
ViewportSize = new ViewportSize
|
||||
{
|
||||
Width = 1280,
|
||||
Height = 720,
|
||||
},
|
||||
Geolocation = new Geolocation
|
||||
{
|
||||
Latitude = 37.819722m,
|
||||
Longitude = -122.478611m,
|
||||
},
|
||||
Permissions = new[] { ContextPermission.Geolocation },
|
||||
UserAgent = "hardkodemium",
|
||||
Locale = "es",
|
||||
ColorScheme = ColorScheme.Dark,
|
||||
TimezoneId = "Europe/Rome",
|
||||
});`;
|
||||
await cli.waitFor(expectedResult);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
|
@ -83,12 +86,13 @@ test('should print the correct context options when using a device', async ({ br
|
|||
test.skip(browserName !== 'chromium');
|
||||
|
||||
const cli = runCLI(['--device=Pixel 2', '--target=csharp', emptyHTML]);
|
||||
const expectedResult = `await Playwright.InstallAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.Chromium.LaunchAsync(
|
||||
${launchOptions(channel)}
|
||||
);
|
||||
var context = await browser.NewContextAsync(playwright.Devices["Pixel 2"]);`;
|
||||
const expectedResult = `
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
|
||||
{
|
||||
${launchOptions(channel)}
|
||||
});
|
||||
var context = await browser.NewContextAsync(playwright.Devices["Pixel 2"]);`;
|
||||
await cli.waitFor(expectedResult);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
});
|
||||
|
|
@ -107,33 +111,34 @@ test('should print the correct context options when using a device and additiona
|
|||
'--viewport-size=1280,720',
|
||||
'--target=csharp',
|
||||
emptyHTML]);
|
||||
const expectedResult = `await Playwright.InstallAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.Webkit.LaunchAsync(
|
||||
${launchOptions(channel)},
|
||||
proxy: new ProxySettings
|
||||
{
|
||||
Server = "http://myproxy:3128",
|
||||
}
|
||||
);
|
||||
var context = await browser.NewContextAsync(new BrowserContextOptions(playwright.Devices["iPhone 11"])
|
||||
{
|
||||
UserAgent = "hardkodemium",
|
||||
Viewport = new ViewportSize
|
||||
{
|
||||
Width = 1280,
|
||||
Height = 720,
|
||||
},
|
||||
Geolocation = new Geolocation
|
||||
{
|
||||
Latitude = 37.819722m,
|
||||
Longitude = -122.478611m,
|
||||
},
|
||||
Permissions = new[] { ContextPermission.Geolocation },
|
||||
Locale = "es",
|
||||
ColorScheme = ColorScheme.Dark,
|
||||
TimezoneId = "Europe/Rome",
|
||||
});`;
|
||||
const expectedResult = `
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
|
||||
{
|
||||
${launchOptions(channel)}
|
||||
Proxy = new ProxySettings
|
||||
{
|
||||
Server = "http://myproxy:3128",
|
||||
},
|
||||
});
|
||||
var context = await browser.NewContextAsync(new BrowserNewContextOptions(playwright.Devices["iPhone 11"])
|
||||
{
|
||||
UserAgent = "hardkodemium",
|
||||
ViewportSize = new ViewportSize
|
||||
{
|
||||
Width = 1280,
|
||||
Height = 720,
|
||||
},
|
||||
Geolocation = new Geolocation
|
||||
{
|
||||
Latitude = 37.819722m,
|
||||
Longitude = -122.478611m,
|
||||
},
|
||||
Permissions = new[] { ContextPermission.Geolocation },
|
||||
Locale = "es",
|
||||
ColorScheme = ColorScheme.Dark,
|
||||
TimezoneId = "Europe/Rome",
|
||||
});`;
|
||||
|
||||
await cli.waitFor(expectedResult);
|
||||
expect(cli.text()).toContain(expectedResult);
|
||||
|
|
@ -144,18 +149,22 @@ test('should print load/save storageState', async ({ browserName, channel, runCL
|
|||
const saveFileName = testInfo.outputPath('save.json');
|
||||
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
|
||||
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=csharp', emptyHTML]);
|
||||
const expectedResult1 = `await Playwright.InstallAsync();
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(
|
||||
${launchOptions(channel)}
|
||||
);
|
||||
var context = await browser.NewContextAsync(
|
||||
storageState: "${loadFileName}");`;
|
||||
const expectedResult1 = `
|
||||
using var playwright = await Playwright.CreateAsync();
|
||||
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
|
||||
{
|
||||
${launchOptions(channel)}
|
||||
});
|
||||
var context = await browser.NewContextAsync(new BrowserNewContextOptions
|
||||
{
|
||||
StorageStatePath = "${loadFileName}",
|
||||
});`;
|
||||
await cli.waitFor(expectedResult1);
|
||||
|
||||
const expectedResult2 = `
|
||||
// ---------------------
|
||||
await context.StorageStateAsync(path: "${saveFileName}");
|
||||
await context.StorageStateAsync(new BrowserContextStorageStateOptions
|
||||
{
|
||||
Path = "${saveFileName}"
|
||||
});
|
||||
`;
|
||||
await cli.waitFor(expectedResult2);
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue