fix(codegen): update csharp scripts to new syntax (#6685)

Drive-by: fix middle/right button clicks in codegen.
This commit is contained in:
Dmitry Gozman 2021-05-20 15:47:14 -07:00 committed by GitHub
parent 08773e836b
commit e4946b79e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 258 additions and 234 deletions

View file

@ -142,6 +142,7 @@ export class Recorder {
removeEventListeners(this._listeners); removeEventListeners(this._listeners);
this._listeners = [ this._listeners = [
addEventListener(document, 'click', event => this._onClick(event as MouseEvent), true), 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, 'input', event => this._onInput(event), true),
addEventListener(document, 'keydown', event => this._onKeyDown(event as KeyboardEvent), true), addEventListener(document, 'keydown', event => this._onKeyDown(event as KeyboardEvent), true),
addEventListener(document, 'keyup', event => this._onKeyUp(event as KeyboardEvent), true), addEventListener(document, 'keyup', event => this._onKeyUp(event as KeyboardEvent), true),

View file

@ -28,7 +28,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
generateAction(actionInContext: ActionInContext): string { generateAction(actionInContext: ActionInContext): string {
const { action, pageAlias } = actionInContext; const { action, pageAlias } = actionInContext;
const formatter = new CSharpFormatter(0); const formatter = new CSharpFormatter(8);
formatter.newLine(); formatter.newLine();
formatter.add('// ' + actionTitle(action)); formatter.add('// ' + actionTitle(action));
@ -41,63 +41,62 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
const subject = actionInContext.isMainFrame ? pageAlias : const subject = actionInContext.isMainFrame ? pageAlias :
(actionInContext.frameName ? (actionInContext.frameName ?
`${pageAlias}.GetFrame(name: ${quote(actionInContext.frameName)})` : `${pageAlias}.Frame(${quote(actionInContext.frameName)})` :
`${pageAlias}.GetFrame(url: ${quote(actionInContext.frameUrl)})`); `${pageAlias}.FrameByUrl(${quote(actionInContext.frameUrl)})`);
const signals = toSignalMap(action); const signals = toSignalMap(action);
if (signals.dialog) { 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}"); Console.WriteLine($"Dialog message: {dialog.Message}");
e.Dialog.DismissAsync(); dialog.DismissAsync();
${pageAlias}.Dialog -= ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler; ${pageAlias}.Dialog -= ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;
} }
${pageAlias}.Dialog += ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;`); ${pageAlias}.Dialog += ${pageAlias}_Dialog${signals.dialog.dialogAlias}_EventHandler;`);
} }
const emitTaskWhenAll = signals.waitForNavigation || signals.popup || signals.download; const lines: string[] = [];
if (emitTaskWhenAll) { const actionCall = this._generateActionCall(action, actionInContext.isMainFrame);
if (signals.popup) if (signals.waitForNavigation) {
formatter.add(`var ${signals.popup.popupAlias}Task = ${pageAlias}.WaitForEventAsync(PageEvent.Popup)`); lines.push(`await Task.WhenAll(`);
else if (signals.download) lines.push(`${pageAlias}.WaitForNavigationAsync(/* new ${actionInContext.isMainFrame ? 'Page' : 'Frame'}WaitForNavigationOptions`);
formatter.add(`var downloadTask = ${pageAlias}.WaitForEventAsync(PageEvent.Download);`); lines.push(`{`);
lines.push(` UrlString = ${quote(signals.waitForNavigation.url)}`);
formatter.add(`await Task.WhenAll(`); lines.push(`} */),`);
lines.push(`${subject}.${actionCall});`);
} else {
lines.push(`await ${subject}.${actionCall};`);
} }
// Popup signals. if (signals.download) {
if (signals.popup) lines.unshift(`var download${signals.download.downloadAlias} = await ${pageAlias}.RunAndWaitForEventAsync(PageEvent.Download, async () =>\n{`);
formatter.add(`${signals.popup.popupAlias}Task,`); lines.push(`});`);
}
// Navigation signal. if (signals.popup) {
if (signals.waitForNavigation) lines.unshift(`var ${signals.popup.popupAlias} = await ${pageAlias}.RunAndWaitForEventAsync(PageEvent.Popup, async () =>\n{`);
formatter.add(`${pageAlias}.WaitForNavigationAsync(/*${quote(signals.waitForNavigation.url)}*/),`); lines.push(`});`);
}
// Download signals. for (const line of lines)
if (signals.download) formatter.add(line);
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}`);
if (signals.assertNavigation) if (signals.assertNavigation)
formatter.add(` // Assert.Equal(${quote(signals.assertNavigation.url)}, ${pageAlias}.Url);`); formatter.add(` // Assert.Equal(${quote(signals.assertNavigation.url)}, ${pageAlias}.Url);`);
return formatter.format(); return formatter.format();
} }
private _generateActionCall(action: Action): string { private _generateActionCall(action: Action, isPage: boolean): string {
switch (action.name) { switch (action.name) {
case 'openPage': case 'openPage':
throw Error('Not reached'); throw Error('Not reached');
case 'closePage': case 'closePage':
return 'CloseAsync()'; return 'CloseAsync()';
case 'click': { case 'click': {
let method = 'ClickAsync'; let method = 'Click';
if (action.clickCount === 2) if (action.clickCount === 2)
method = 'DblClickAsync'; method = 'DblClick';
const modifiers = toModifiers(action.modifiers); const modifiers = toModifiers(action.modifiers);
const options: MouseClickOptions = {}; const options: MouseClickOptions = {};
if (action.button !== 'left') if (action.button !== 'left')
@ -106,8 +105,10 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
options.modifiers = modifiers; options.modifiers = modifiers;
if (action.clickCount > 2) if (action.clickCount > 2)
options.clickCount = action.clickCount; options.clickCount = action.clickCount;
const optionsString = formatOptions(options, true, false); if (!Object.entries(options).length)
return `${method}(${quote(action.selector)}${optionsString})`; return `${method}Async(${quote(action.selector)})`;
const optionsString = formatObject(options, ' ', (isPage ? 'Page' : 'Frame') + method + 'Options');
return `${method}Async(${quote(action.selector)}, ${optionsString})`;
} }
case 'check': case 'check':
return `CheckAsync(${quote(action.selector)})`; return `CheckAsync(${quote(action.selector)})`;
@ -116,7 +117,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
case 'fill': case 'fill':
return `FillAsync(${quote(action.selector)}, ${quote(action.text)})`; return `FillAsync(${quote(action.selector)}, ${quote(action.text)})`;
case 'setInputFiles': 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': { case 'press': {
const modifiers = toModifiers(action.modifiers); const modifiers = toModifiers(action.modifiers);
const shortcut = [...modifiers, action.key].join('+'); const shortcut = [...modifiers, action.key].join('+');
@ -125,68 +126,37 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
case 'navigate': case 'navigate':
return `GotoAsync(${quote(action.url)})`; return `GotoAsync(${quote(action.url)})`;
case 'select': 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 { generateHeader(options: LanguageGeneratorOptions): string {
const formatter = new CSharpFormatter(0); const formatter = new CSharpFormatter(0);
formatter.add(` formatter.add(`
await Playwright.InstallAsync(); using System;
using System.Threading.Tasks;
using Microsoft.Playwright;
class Example
{
static async Task Main(string[] args)
{
using var playwright = await Playwright.CreateAsync(); using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatArgs(options.launchOptions)} await using var browser = await playwright.${toPascal(options.browserName)}.LaunchAsync(${formatObject(options.launchOptions, ' ', 'BrowserTypeLaunchOptions')});
);
var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`); var context = await browser.NewContextAsync(${formatContextOptions(options.contextOptions, options.deviceName)});`);
return formatter.format(); return formatter.format();
} }
generateFooter(saveStorage: string | undefined): string { generateFooter(saveStorage: string | undefined): string {
const storageStateLine = saveStorage ? `\nawait context.StorageStateAsync(path: "${saveStorage}");` : ''; const storageStateLine = saveStorage ? `\n await context.StorageStateAsync(new BrowserContextStorageStateOptions\n {\n Path = ${quote(saveStorage)}\n });\n` : '';
return `\n// ---------------------${storageStateLine}`; 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 { function formatObject(value: any, indent = ' ', name = ''): string {
if (typeof value === 'string') { if (typeof value === 'string') {
if (name === 'permissions' || name === 'colorScheme') if (['permissions', 'colorScheme', 'modifiers', 'button'].includes(name))
return `${getClassName(name)}.${toPascal(value)}`; return `${getClassName(name)}.${toPascal(value)}`;
return quote(value); return quote(value);
} }
@ -195,10 +165,12 @@ function formatObject(value: any, indent = ' ', name = ''): string {
if (typeof value === 'object') { if (typeof value === 'object') {
const keys = Object.keys(value); const keys = Object.keys(value);
if (!keys.length) if (!keys.length)
return ''; return name ? `new ${getClassName(name)}` : '';
const tokens: string[] = []; const tokens: string[] = [];
for (const key of keys) for (const key of keys) {
tokens.push(`${toPascal(key)} = ${formatObject(value[key], indent, key)},`); const property = getPropertyName(key);
tokens.push(`${property} = ${formatObject(value[key], indent, key)},`);
}
if (name) if (name)
return `new ${getClassName(name)}\n{\n${indent}${tokens.join(`\n${indent}`)}\n${indent}}`; return `new ${getClassName(name)}\n{\n${indent}${tokens.join(`\n${indent}`)}\n${indent}}`;
return `{\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 'viewport': return 'ViewportSize';
case 'proxy': return 'ProxySettings'; case 'proxy': return 'ProxySettings';
case 'permissions': return 'ContextPermission'; case 'permissions': return 'ContextPermission';
case 'modifiers': return 'KeyboardModifier';
case 'button': return 'MouseButton';
default: return toPascal(value); 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 { function toPascal(value: string): string {
return value[0].toUpperCase() + value.slice(1); return value[0].toUpperCase() + value.slice(1);
} }
function formatContextOptions(options: BrowserContextOptions, deviceName: string | undefined): string { function formatContextOptions(options: BrowserContextOptions, deviceName: string | undefined): string {
const device = deviceName && deviceDescriptors[deviceName]; const device = deviceName && deviceDescriptors[deviceName];
if (!device) if (!device) {
return formatArgs(options); if (!Object.entries(options).length)
const serializedObject = formatObject(sanitizeDeviceOptions(device, options), ' '); return '';
// When there are no additional context options, we still want to spread the device inside. return formatObject(options, ' ', 'BrowserNewContextOptions');
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')}`;
} }
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 { class CSharpFormatter {
@ -278,7 +256,7 @@ class CSharpFormatter {
line = spaces + extraSpaces + line; line = spaces + extraSpaces + line;
if (line.endsWith('{') || line.endsWith('[') || line.endsWith('(')) if (line.endsWith('{') || line.endsWith('[') || line.endsWith('('))
spaces += this._baseIndent; spaces += this._baseIndent;
if (line.endsWith('});')) if (line.endsWith('));'))
spaces = spaces.substring(this._baseIndent.length); spaces = spaces.substring(this._baseIndent.length);
return this._baseOffset + line; return this._baseOffset + line;

View file

@ -108,6 +108,7 @@ export type PopupSignal = BaseSignal & {
export type DownloadSignal = BaseSignal & { export type DownloadSignal = BaseSignal & {
name: 'download', name: 'download',
downloadAlias: string,
}; };
export type DialogSignal = BaseSignal & { export type DialogSignal = BaseSignal & {

View file

@ -46,6 +46,7 @@ export class RecorderSupplement implements InstrumentationListener {
private _pageAliases = new Map<Page, string>(); private _pageAliases = new Map<Page, string>();
private _lastPopupOrdinal = 0; private _lastPopupOrdinal = 0;
private _lastDialogOrdinal = 0; private _lastDialogOrdinal = 0;
private _lastDownloadOrdinal = 0;
private _timers = new Set<NodeJS.Timeout>(); private _timers = new Set<NodeJS.Timeout>();
private _context: BrowserContext; private _context: BrowserContext;
private _mode: Mode; private _mode: Mode;
@ -381,7 +382,7 @@ export class RecorderSupplement implements InstrumentationListener {
} }
private _onDownload(page: Page) { private _onDownload(page: Page) {
const pageAlias = this._pageAliases.get(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) { private _onDialog(page: Page) {

View file

@ -464,7 +464,7 @@ await page.UncheckAsync(\"input[name=\\\"accept\\\"]\");`);
expect(sources.get('<csharp>').text).toContain(` expect(sources.get('<csharp>').text).toContain(`
// Select 2 // Select 2
await page.SelectOptionAsync(\"select\", \"2\");`); await page.SelectOptionAsync(\"select\", new[] { \"2\" });`);
expect(message.text()).toBe('2'); expect(message.text()).toBe('2');
}); });
@ -510,10 +510,10 @@ await page.SelectOptionAsync(\"select\", \"2\");`);
page1 = await popup_info.value`); page1 = await popup_info.value`);
expect(sources.get('<csharp>').text).toContain(` expect(sources.get('<csharp>').text).toContain(`
var page1Task = page.WaitForEventAsync(PageEvent.Popup) var page1 = await page.RunAndWaitForEventAsync(PageEvent.Popup, async () =>
await Task.WhenAll( {
page1Task, await page.ClickAsync(\"text=link\");
page.ClickAsync(\"text=link\"));`); });`);
expect(popup.url()).toBe('about:blank'); expect(popup.url()).toBe('about:blank');
}); });
@ -603,7 +603,10 @@ await page.ClickAsync(\"text=link\");
expect(sources.get('<csharp>').text).toContain(` expect(sources.get('<csharp>').text).toContain(`
// Click text=link // Click text=link
await Task.WhenAll( await Task.WhenAll(
page.WaitForNavigationAsync(/*\"about:blank#foo\"*/), page.WaitForNavigationAsync(/* new PageWaitForNavigationOptions
{
UrlString = \"about:blank#foo\"
} */),
page.ClickAsync(\"text=link\"));`); page.ClickAsync(\"text=link\"));`);
expect(page.url()).toContain('about:blank#foo'); 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).not.toContain(`await page.press('input', 'AltGraph');`);
expect(recorder.sources().get('<javascript>').text).toContain(`await page.fill('input', 'playwright@example.com');`); 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,
});`);
});
}); });

View file

@ -148,8 +148,7 @@ await page.CloseAsync();`);
expect(sources.get('<csharp>').text).toContain(` expect(sources.get('<csharp>').text).toContain(`
// Upload file-to-upload.txt // Upload file-to-upload.txt
await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", \"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 }) => { test('should upload multiple files', async ({ page, openRecorder, browserName, asset }) => {
@ -278,10 +277,10 @@ await page.SetInputFilesAsync(\"input[type=\\\"file\\\"]\", new[] { });`);
expect(sources.get('<csharp>').text).toContain(` expect(sources.get('<csharp>').text).toContain(`
// Click text=Download // Click text=Download
var downloadTask = page.WaitForEventAsync(PageEvent.Download); var download1 = await page.RunAndWaitForEventAsync(PageEvent.Download, async () =>
await Task.WhenAll( {
downloadTask, await page.ClickAsync(\"text=Download\");
page.ClickAsync(\"text=Download\"));`); });`);
}); });
test('should handle dialogs', async ({ page, openRecorder }) => { test('should handle dialogs', async ({ page, openRecorder }) => {
@ -326,10 +325,10 @@ await Task.WhenAll(
expect(sources.get('<csharp>').text).toContain(` expect(sources.get('<csharp>').text).toContain(`
// Click text=click me // Click text=click me
void page_Dialog1_EventHandler(object sender, DialogEventArgs e) void page_Dialog1_EventHandler(object sender, IDialog dialog)
{ {
Console.WriteLine($\"Dialog message: {e.Dialog.Message}\"); Console.WriteLine($\"Dialog message: {dialog.Message}\");
e.Dialog.DismissAsync(); dialog.DismissAsync();
page.Dialog -= page_Dialog1_EventHandler; page.Dialog -= page_Dialog1_EventHandler;
} }
page.Dialog += page_Dialog1_EventHandler; page.Dialog += page_Dialog1_EventHandler;
@ -522,7 +521,7 @@ await page1.GotoAsync("about:blank?foo");`);
expect(sources.get('<csharp>').text).toContain(` expect(sources.get('<csharp>').text).toContain(`
// Click text=Hi, I'm frame // Click text=Hi, I'm frame
await page.GetFrame(name: \"one\").ClickAsync(\"text=Hi, I'm frame\");`); await page.Frame(\"one\").ClickAsync(\"text=Hi, I'm frame\");`);
[sources] = await Promise.all([ [sources] = await Promise.all([
recorder.waitForOutput('<javascript>', 'two'), recorder.waitForOutput('<javascript>', 'two'),
@ -549,7 +548,7 @@ await page.GetFrame(name: \"one\").ClickAsync(\"text=Hi, I'm frame\");`);
expect(sources.get('<csharp>').text).toContain(` expect(sources.get('<csharp>').text).toContain(`
// Click text=Hi, I'm frame // Click text=Hi, I'm frame
await page.GetFrame(name: \"two\").ClickAsync(\"text=Hi, I'm frame\");`); await page.Frame(\"two\").ClickAsync(\"text=Hi, I'm frame\");`);
[sources] = await Promise.all([ [sources] = await Promise.all([
recorder.waitForOutput('<javascript>', 'url: \''), recorder.waitForOutput('<javascript>', 'url: \''),
@ -576,7 +575,7 @@ await page.GetFrame(name: \"two\").ClickAsync(\"text=Hi, I'm frame\");`);
expect(sources.get('<csharp>').text).toContain(` expect(sources.get('<csharp>').text).toContain(`
// Click text=Hi, I'm frame // Click text=Hi, I'm frame
await page.GetFrame(url: \"http://localhost:${server.PORT}/frames/frame.html\").ClickAsync(\"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 }) => { test('should record navigations after identical pushState', async ({ page, openRecorder, server }) => {

View file

@ -20,7 +20,7 @@ import { test, expect } from './inspectorTest';
const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString(); const emptyHTML = new URL('file://' + path.join(__dirname, '..', 'assets', 'empty.html')).toString();
const launchOptions = (channel: string) => { 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 { function capitalize(browserName: string): string {
@ -29,11 +29,11 @@ function capitalize(browserName: string): string {
test('should print the correct imports and context options', async ({ browserName, channel, runCLI }) => { test('should print the correct imports and context options', async ({ browserName, channel, runCLI }) => {
const cli = runCLI(['--target=csharp', emptyHTML]); const cli = runCLI(['--target=csharp', emptyHTML]);
const expectedResult = `await Playwright.InstallAsync(); const expectedResult = ` using var playwright = await Playwright.CreateAsync();
using var playwright = await Playwright.CreateAsync(); await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync( {
${launchOptions(channel)} ${launchOptions(channel)}
); });
var context = await browser.NewContextAsync();`; var context = await browser.NewContextAsync();`;
await cli.waitFor(expectedResult).catch(e => e); await cli.waitFor(expectedResult).catch(e => e);
expect(cli.text()).toContain(expectedResult); 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', '--viewport-size=1280,720',
'--target=csharp', '--target=csharp',
emptyHTML]); emptyHTML]);
const expectedResult = `await Playwright.InstallAsync(); const expectedResult = `
using var playwright = await Playwright.CreateAsync(); using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync( await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
${launchOptions(channel)}, {
proxy: new ProxySettings ${launchOptions(channel)}
Proxy = new ProxySettings
{ {
Server = "http://myproxy:3128", Server = "http://myproxy:3128",
} },
); });
var context = await browser.NewContextAsync( var context = await browser.NewContextAsync(new BrowserNewContextOptions
viewport: new ViewportSize {
ViewportSize = new ViewportSize
{ {
Width = 1280, Width = 1280,
Height = 720, Height = 720,
}, },
geolocation: new Geolocation Geolocation = new Geolocation
{ {
Latitude = 37.819722m, Latitude = 37.819722m,
Longitude = -122.478611m, Longitude = -122.478611m,
}, },
permissions: new[] { ContextPermission.Geolocation }, Permissions = new[] { ContextPermission.Geolocation },
userAgent: "hardkodemium", UserAgent = "hardkodemium",
locale: "es", Locale = "es",
colorScheme: ColorScheme.Dark, ColorScheme = ColorScheme.Dark,
timezoneId: "Europe/Rome");`; TimezoneId = "Europe/Rome",
});`;
await cli.waitFor(expectedResult); await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult); expect(cli.text()).toContain(expectedResult);
}); });
@ -83,11 +86,12 @@ test('should print the correct context options when using a device', async ({ br
test.skip(browserName !== 'chromium'); test.skip(browserName !== 'chromium');
const cli = runCLI(['--device=Pixel 2', '--target=csharp', emptyHTML]); const cli = runCLI(['--device=Pixel 2', '--target=csharp', emptyHTML]);
const expectedResult = `await Playwright.InstallAsync(); const expectedResult = `
using var playwright = await Playwright.CreateAsync(); using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync( await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
{
${launchOptions(channel)} ${launchOptions(channel)}
); });
var context = await browser.NewContextAsync(playwright.Devices["Pixel 2"]);`; var context = await browser.NewContextAsync(playwright.Devices["Pixel 2"]);`;
await cli.waitFor(expectedResult); await cli.waitFor(expectedResult);
expect(cli.text()).toContain(expectedResult); expect(cli.text()).toContain(expectedResult);
@ -107,19 +111,20 @@ test('should print the correct context options when using a device and additiona
'--viewport-size=1280,720', '--viewport-size=1280,720',
'--target=csharp', '--target=csharp',
emptyHTML]); emptyHTML]);
const expectedResult = `await Playwright.InstallAsync(); const expectedResult = `
using var playwright = await Playwright.CreateAsync(); using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Webkit.LaunchAsync( await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
${launchOptions(channel)}, {
proxy: new ProxySettings ${launchOptions(channel)}
Proxy = new ProxySettings
{ {
Server = "http://myproxy:3128", Server = "http://myproxy:3128",
} },
); });
var context = await browser.NewContextAsync(new BrowserContextOptions(playwright.Devices["iPhone 11"]) var context = await browser.NewContextAsync(new BrowserNewContextOptions(playwright.Devices["iPhone 11"])
{ {
UserAgent = "hardkodemium", UserAgent = "hardkodemium",
Viewport = new ViewportSize ViewportSize = new ViewportSize
{ {
Width = 1280, Width = 1280,
Height = 720, Height = 720,
@ -144,18 +149,22 @@ test('should print load/save storageState', async ({ browserName, channel, runCL
const saveFileName = testInfo.outputPath('save.json'); const saveFileName = testInfo.outputPath('save.json');
await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8'); await fs.promises.writeFile(loadFileName, JSON.stringify({ cookies: [], origins: [] }), 'utf8');
const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=csharp', emptyHTML]); const cli = runCLI([`--load-storage=${loadFileName}`, `--save-storage=${saveFileName}`, '--target=csharp', emptyHTML]);
const expectedResult1 = `await Playwright.InstallAsync(); const expectedResult1 = `
using var playwright = await Playwright.CreateAsync(); using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync( await using var browser = await playwright.${capitalize(browserName)}.LaunchAsync(new BrowserTypeLaunchOptions
{
${launchOptions(channel)} ${launchOptions(channel)}
); });
var context = await browser.NewContextAsync( var context = await browser.NewContextAsync(new BrowserNewContextOptions
storageState: "${loadFileName}");`; {
StorageStatePath = "${loadFileName}",
});`;
await cli.waitFor(expectedResult1); await cli.waitFor(expectedResult1);
const expectedResult2 = ` const expectedResult2 = `
// --------------------- await context.StorageStateAsync(new BrowserContextStorageStateOptions
await context.StorageStateAsync(path: "${saveFileName}"); {
Path = "${saveFileName}"
});
`; `;
await cli.waitFor(expectedResult2); await cli.waitFor(expectedResult2);
}); });