From e4946b79e6d65ffd2f56760e7b14cc0c127c3c9b Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Thu, 20 May 2021 15:47:14 -0700 Subject: [PATCH] fix(codegen): update csharp scripts to new syntax (#6685) Drive-by: fix middle/right button clicks in codegen. --- src/server/supplements/injected/recorder.ts | 1 + src/server/supplements/recorder/csharp.ts | 174 ++++++++---------- .../supplements/recorder/recorderActions.ts | 1 + src/server/supplements/recorderSupplement.ts | 3 +- tests/inspector/cli-codegen-1.spec.ts | 85 ++++++--- tests/inspector/cli-codegen-2.spec.ts | 69 ++++--- tests/inspector/cli-codegen-csharp.spec.ts | 159 ++++++++-------- 7 files changed, 258 insertions(+), 234 deletions(-) diff --git a/src/server/supplements/injected/recorder.ts b/src/server/supplements/injected/recorder.ts index 7e0ca50ee3..6f9db0f2d8 100644 --- a/src/server/supplements/injected/recorder.ts +++ b/src/server/supplements/injected/recorder.ts @@ -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), diff --git a/src/server/supplements/recorder/csharp.ts b/src/server/supplements/recorder/csharp.ts index f8265ce2a3..5df0717adc 100644 --- a/src/server/supplements/recorder/csharp.ts +++ b/src/server/supplements/recorder/csharp.ts @@ -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; diff --git a/src/server/supplements/recorder/recorderActions.ts b/src/server/supplements/recorder/recorderActions.ts index f119e5564f..027057e828 100644 --- a/src/server/supplements/recorder/recorderActions.ts +++ b/src/server/supplements/recorder/recorderActions.ts @@ -108,6 +108,7 @@ export type PopupSignal = BaseSignal & { export type DownloadSignal = BaseSignal & { name: 'download', + downloadAlias: string, }; export type DialogSignal = BaseSignal & { diff --git a/src/server/supplements/recorderSupplement.ts b/src/server/supplements/recorderSupplement.ts index ee19e7f1da..e237fdb2d6 100644 --- a/src/server/supplements/recorderSupplement.ts +++ b/src/server/supplements/recorderSupplement.ts @@ -46,6 +46,7 @@ export class RecorderSupplement implements InstrumentationListener { private _pageAliases = new Map(); private _lastPopupOrdinal = 0; private _lastDialogOrdinal = 0; + private _lastDownloadOrdinal = 0; private _timers = new Set(); 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) { diff --git a/tests/inspector/cli-codegen-1.spec.ts b/tests/inspector/cli-codegen-1.spec.ts index 4c16470d7d..44004b0bea 100644 --- a/tests/inspector/cli-codegen-1.spec.ts +++ b/tests/inspector/cli-codegen-1.spec.ts @@ -51,8 +51,8 @@ test.describe('cli codegen', () => { page.click("text=Submit");`); expect(sources.get('').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('').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('').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('').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('').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('').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('').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('').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('').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('').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('').text).not.toContain(`await page.press('input', 'AltGraph');`); expect(recorder.sources().get('').text).toContain(`await page.fill('input', 'playwright@example.com');`); }); + + test('should middle click', async ({ page, openRecorder, server }) => { + const recorder = await openRecorder(); + + await recorder.setContentAndWait(`Click me`); + + const [sources] = await Promise.all([ + recorder.waitForOutput('', 'click'), + page.click('a', { button: 'middle' }), + ]); + + expect(sources.get('').text).toContain(` + await page.click('text=Click me', { + button: 'middle' + });`); + + expect(sources.get('').text).toContain(` + page.click("text=Click me", button="middle")`); + + expect(sources.get('').text).toContain(` + await page.click("text=Click me", button="middle")`); + + // TODO: fix this for java + // expect(sources.get('').text).toContain(` + // page.click("text=Click me", foo);`); + + expect(sources.get('').text).toContain(` + await page.ClickAsync("text=Click me", new PageClickOptions + { + Button = MouseButton.Middle, + });`); + }); }); diff --git a/tests/inspector/cli-codegen-2.spec.ts b/tests/inspector/cli-codegen-2.spec.ts index f61421f989..93597e5544 100644 --- a/tests/inspector/cli-codegen-2.spec.ts +++ b/tests/inspector/cli-codegen-2.spec.ts @@ -43,8 +43,8 @@ test.describe('cli codegen', () => { page = await context.new_page()`); expect(sources.get('').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('').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('').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('').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('').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('').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('').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('').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('').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('').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('').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('', '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('').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('', '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('').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 }) => { diff --git a/tests/inspector/cli-codegen-csharp.spec.ts b/tests/inspector/cli-codegen-csharp.spec.ts index 16e9b02944..fd85744464 100644 --- a/tests/inspector/cli-codegen-csharp.spec.ts +++ b/tests/inspector/cli-codegen-csharp.spec.ts @@ -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); });