diff --git a/docs/src/api/class-browsercontext.md b/docs/src/api/class-browsercontext.md index 303738d456..39ce7f19ca 100644 --- a/docs/src/api/class-browsercontext.md +++ b/docs/src/api/class-browsercontext.md @@ -132,7 +132,7 @@ print(page.evaluate("location.href")) ``` ```csharp -var popup = await context.RunAndWaitForEventAsync(BrowserContextEvent.Page, async => +var popup = await context.RunAndWaitForPageAsync(async => { await page.ClickAsync("a"); }); @@ -1194,9 +1194,8 @@ Optional handler function used to register a routing with [`method: BrowserConte Optional handler function used to register a routing with [`method: BrowserContext.route`]. ## async method: BrowserContext.waitForEvent -* langs: csharp, js, python +* langs: js, python - alias-python: expect_event - - alias-csharp: RunAndWaitForEventAsync - returns: <[any]> Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy @@ -1226,7 +1225,7 @@ page = event_info.value ``` ```csharp -var page = await context.RunAndWaitForEventAsync(ContextEvent.Page, async () => +var page = await context.RunAndWaitForPageAsync(async () => { await page.ClickAsync("button"); }); @@ -1247,8 +1246,9 @@ Event name, same one would pass into `browserContext.on(event)`. Either a predicate that receives an event or an options object. Optional. ## async method: BrowserContext.waitForPage -* langs: java, python +* langs: java, python, csharp - alias-python: expect_page + - alias-csharp: RunAndWaitForPage - returns: <[Page]> Performs action and waits for a new [Page] to be created in the context. If predicate is provided, it passes @@ -1264,9 +1264,8 @@ Receives the [Page] object and resolves to truthy value when the waiting should ### option: BrowserContext.waitForPage.timeout = %%-wait-for-event-timeout-%% ## async method: BrowserContext.waitForEvent2 -* langs: python, csharp +* langs: python - alias-python: wait_for_event - - alias-csharp: WaitForEventAsync - returns: <[any]> :::note diff --git a/docs/src/api/class-download.md b/docs/src/api/class-download.md index 54196b5a6c..e00b6c4727 100644 --- a/docs/src/api/class-download.md +++ b/docs/src/api/class-download.md @@ -49,7 +49,7 @@ path = download.path() ``` ```csharp -var download = await page.RunAndWaitForEventAsync(PageEvent.Download, async () => +var download = await page.RunAndWaitForDownloadAsync(async () => { await page.ClickAsync("#downloadButton"); }); diff --git a/docs/src/api/class-filechooser.md b/docs/src/api/class-filechooser.md index d905e68631..a3afa27354 100644 --- a/docs/src/api/class-filechooser.md +++ b/docs/src/api/class-filechooser.md @@ -30,7 +30,7 @@ file_chooser.set_files("myfile.pdf") ``` ```csharp -var fileChooser = await page.RunAndWaitForEventAsync(Page.FileChooser, async () => +var fileChooser = await page.RunAndWaitForFileChooserAsync(async () => { await page.ClickAsync("upload"); }); diff --git a/docs/src/api/class-frame.md b/docs/src/api/class-frame.md index 50527c4dbf..b1cc98cbad 100644 --- a/docs/src/api/class-frame.md +++ b/docs/src/api/class-frame.md @@ -1387,6 +1387,7 @@ await frame.WaitForLoadStateAsync(); // Defaults to LoadState.Load ## async method: Frame.waitForNavigation * langs: * alias-python: expect_navigation + * alias-csharp: RunAndWaitForNavigation - returns: <[null]|[Response]> Waits for the frame navigation and returns the main resource response. In case of multiple redirects, the navigation diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 6813c7c364..528c6a97c1 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -266,7 +266,7 @@ try { // Crash might happen during a click. await page.ClickAsync("button"); // Or while waiting for an event. - await page.WaitForEventAsync(PageEvent.Popup); + await page.WaitForPopup(); } catch (PlaywrightException e) { // When the page crashes, exception message contains "crash". } @@ -397,7 +397,7 @@ print(popup.evaluate("location.href")) ``` ```csharp -var popup = await page.RunAndWaitForEventAsync(PageEvent.Popup, async () => +var popup = await page.RunAndWaitForPopupAsync(async () => { await page.EvaluateAsync("() => window.open('https://microsoft.com')"); }); @@ -2933,13 +2933,14 @@ Performs action and waits for the Page to close. ### option: Page.waitForClose.timeout = %%-wait-for-event-timeout-%% ## async method: Page.waitForConsoleMessage -* langs: java, python +* langs: java, python, csharp - alias-python: expect_console_message + - alias-csharp: RunAndWaitForConsoleMessage - returns: <[ConsoleMessage]> Performs action and waits for a [ConsoleMessage] to be logged by in the page. If predicate is provided, it passes [ConsoleMessage] value into the `predicate` function and waits for `predicate(message)` to return a truthy value. -Will throw an error if the page is closed before the console event is fired. +Will throw an error if the page is closed before the [`event: Page.console`] event is fired. ### option: Page.waitForConsoleMessage.predicate = - `predicate` <[function]\([ConsoleMessage]\):[boolean]> @@ -2949,8 +2950,9 @@ Receives the [ConsoleMessage] object and resolves to truthy value when the waiti ### option: Page.waitForConsoleMessage.timeout = %%-wait-for-event-timeout-%% ## async method: Page.waitForDownload -* langs: java, python +* langs: java, python, csharp - alias-python: expect_download + - alias-csharp: RunAndWaitForDownload - returns: <[Download]> Performs action and waits for a new [Download]. If predicate is provided, it passes @@ -2965,9 +2967,8 @@ Receives the [Download] object and resolves to truthy value when the waiting sho ### option: Page.waitForDownload.timeout = %%-wait-for-event-timeout-%% ## async method: Page.waitForEvent -* langs: csharp, js, python +* langs: js, python - alias-python: expect_event - - alias-csharp: RunAndWaitForEventAsync - returns: <[any]> Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy @@ -2992,13 +2993,6 @@ with page.expect_event("framenavigated") as event_info: frame = event_info.value ``` -```csharp -var frame = await page.RunAndWaitForEventAsync(PageEvent.FrameNavigated, async () => -{ - await page.ClickAsync("button"); -} -``` - ### param: Page.waitForEvent.event = %%-wait-for-event-event-%% ### param: Page.waitForEvent.optionsOrPredicate @@ -3011,8 +3005,9 @@ var frame = await page.RunAndWaitForEventAsync(PageEvent.FrameNavigated, async ( Either a predicate that receives an event or an options object. Optional. ## async method: Page.waitForFileChooser -* langs: java, python +* langs: java, python, csharp - alias-python: expect_file_chooser + - alias-csharp: RunAndWaitForFileChooser - returns: <[FileChooser]> Performs action and waits for a new [FileChooser] to be created. If predicate is provided, it passes @@ -3223,7 +3218,7 @@ print(popup.title()) # popup is ready to use. ``` ```csharp -var popup = await page.RunAndWaitForEventAsync(PageEvent.Popup, async () => +var popup = await page.RunAndWaitForPopupAsync(async () => { await page.ClickAsync("button"); // click triggers the popup/ }); @@ -3240,6 +3235,7 @@ Shortcut for main frame's [`method: Frame.waitForLoadState`]. ## async method: Page.waitForNavigation * langs: * alias-python: expect_navigation + * alias-csharp: RunAndWaitForNavigation - returns: <[null]|[Response]> Waits for the main frame navigation and returns the main resource response. In case of multiple redirects, the navigation @@ -3296,8 +3292,9 @@ Shortcut for main frame's [`method: Frame.waitForNavigation`]. ### option: Page.waitForNavigation.timeout = %%-navigation-timeout-%% ## async method: Page.waitForPopup -* langs: java, python +* langs: java, python, csharp - alias-python: expect_popup + - alias-csharp: RunAndWaitForPopup - returns: <[Page]> Performs action and waits for a popup [Page]. If predicate is provided, it passes @@ -3314,6 +3311,7 @@ Receives the [Page] object and resolves to truthy value when the waiting should ## async method: Page.waitForRequest * langs: * alias-python: expect_request + * alias-csharp: RunAndWaitForRequest - returns: <[Request]> Waits for the matching request and returns it. See [waiting for event](./events.md#waiting-for-event) for more details about events. @@ -3387,6 +3385,8 @@ await Task.WhenAll(page.WaitForRequestAsync(r => "https://example.com".Equals(r. await page.waitForRequest(request => request.url().searchParams.get('foo') === 'bar' && request.url().searchParams.get('foo2') === 'bar2'); ``` +### param: Page.waitForRequest.action = %%-csharp-wait-for-event-action-%% + ### param: Page.waitForRequest.urlOrPredicate - `urlOrPredicate` <[string]|[RegExp]|[function]\([Request]\):[boolean]> @@ -3404,9 +3404,29 @@ Request URL string, regex or predicate receiving [Request] object. Maximum wait time in milliseconds, defaults to 30 seconds, pass `0` to disable the timeout. The default value can be changed by using the [`method: Page.setDefaultTimeout`] method. + +## async method: Page.waitForRequestFinished +* langs: java, python, csharp + - alias-python: expect_request_finished + - alias-csharp: RunAndWaitForRequestFinished +- returns: <[Request]> + +Performs action and waits for a [Request] to finish loading. If predicate is provided, it passes +[Request] value into the `predicate` function and waits for `predicate(request)` to return a truthy value. +Will throw an error if the page is closed before the [`event: Page.requestFinished`] event is fired. + +### option: Page.waitForRequestFinished.predicate = +- `predicate` <[function]\([Request]\):[boolean]> + +Receives the [ConsoleMessage] object and resolves to truthy value when the waiting should resolve. + +### option: Page.waitForRequestFinished.timeout = %%-wait-for-event-timeout-%% + + ## async method: Page.waitForResponse * langs: * alias-python: expect_response + * alias-csharp: RunAndWaitForResponse - returns: <[Response]> Returns the matched response. See [waiting for event](./events.md#waiting-for-event) for more details about events. @@ -3480,6 +3500,8 @@ await Task.WhenAll(page.WaitForResponseAsync(r => "https://example.com".Equals(r page.ClickAsync("button.triggers-response")); ``` +### param: Page.waitForResponse.action = %%-csharp-wait-for-event-action-%% + ### param: Page.waitForResponse.urlOrPredicate - `urlOrPredicate` <[string]|[RegExp]|[function]\([Response]\):[boolean]> @@ -3688,7 +3710,9 @@ Shortcut for main frame's [`method: Frame.waitForURL`]. ### option: Page.waitForURL.waitUntil = %%-navigation-wait-until-%% ## async method: Page.waitForWebSocket -* langs: java +* langs: java, python, csharp + - alias-python: expect_websocket + - alias-csharp: RunAndWaitForWebSocket - returns: <[WebSocket]> Performs action and waits for a new [WebSocket]. If predicate is provided, it passes @@ -3703,8 +3727,9 @@ Receives the [WebSocket] object and resolves to truthy value when the waiting sh ### option: Page.waitForWebSocket.timeout = %%-wait-for-event-timeout-%% ## async method: Page.waitForWorker -* langs: java, python +* langs: java, python, csharp - alias-python: expect_worker + - alias-csharp: RunAndWaitForWorker - returns: <[Worker]> Performs action and waits for a new [Worker]. If predicate is provided, it passes @@ -3729,9 +3754,8 @@ This does not contain ServiceWorkers ::: ## async method: Page.waitForEvent2 -* langs: python, csharp +* langs: python - alias-python: wait_for_event - - alias-csharp: WaitForEventAsync - returns: <[any]> :::note diff --git a/docs/src/api/class-request.md b/docs/src/api/class-request.md index d91a1cb93e..05063f4486 100644 --- a/docs/src/api/class-request.md +++ b/docs/src/api/class-request.md @@ -239,7 +239,7 @@ print(request.timing) ``` ```csharp -var request = await page.RunAndWaitForEventAsync(PageEvent.RequestFinished, async () => +var request = await page.RunAndWaitForRequestFinishedAsync(async () => { await page.GotoAsync("https://www.microsoft.com"); }); diff --git a/docs/src/api/csharp.md b/docs/src/api/csharp.md index 2a8e24ec05..4d25b81cbd 100644 --- a/docs/src/api/csharp.md +++ b/docs/src/api/csharp.md @@ -6,3 +6,14 @@ Returns parsed request's body for `form-urlencoded` and JSON as a fallback if an When the response is `application/x-www-form-urlencoded` then a key/value object of the values will be returned. Otherwise it will be parsed as JSON. + +### param: BrowserContext.waitForPage.action = %%-csharp-wait-for-event-action-%% +### param: Frame.waitForNavigation.action = %%-csharp-wait-for-event-action-%% +### param: Page.waitForConsoleMessage.action = %%-csharp-wait-for-event-action-%% +### param: Page.waitForDownload.action = %%-csharp-wait-for-event-action-%% +### param: Page.waitForFileChooser.action = %%-csharp-wait-for-event-action-%% +### param: Page.waitForPopup.action = %%-csharp-wait-for-event-action-%% +### param: Page.waitForRequestFinished.action = %%-csharp-wait-for-event-action-%% +### param: Page.waitForNavigation.action = %%-csharp-wait-for-event-action-%% +### param: Page.waitForWebSocket.action = %%-csharp-wait-for-event-action-%% +### param: Page.waitForWorker.action = %%-csharp-wait-for-event-action-%% diff --git a/docs/src/api/params.md b/docs/src/api/params.md index fdb9a75dee..1b4d139fab 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -518,6 +518,12 @@ Specify screenshot type, defaults to `png`. Callback that performs the action triggering the event. +## csharp-wait-for-event-action +* langs: csharp +- `action` <[Func]> + +Action that triggers the event. + ## python-select-options-element * langs: python - `element` <[ElementHandle]|[Array]<[ElementHandle]>> diff --git a/docs/src/events.md b/docs/src/events.md index e5a075dcf1..53c75715c2 100644 --- a/docs/src/events.md +++ b/docs/src/events.md @@ -92,7 +92,7 @@ popup.value.goto("https://wikipedia.org") ``` ```csharp -var popup = await page.RunAndWaitForEventAsync(PageEvent.Popup, async => +var popup = await page.RunAndWaitForPopupAsync(async => { await page.EvaluateAsync("window.open()"); }); diff --git a/docs/src/input.md b/docs/src/input.md index f760e0fba1..6c14ca76ec 100644 --- a/docs/src/input.md +++ b/docs/src/input.md @@ -750,7 +750,7 @@ file_chooser.set_files("myfile.pdf") ``` ```csharp -var fileChooser = page.RunAndWaitForAsync(PageEvent.FileChooser, async () => +var fileChooser = page.RunAndWaitForFileChooserAsync(async () => { await page.ClickAsync("upload"); }); diff --git a/docs/src/multi-pages.md b/docs/src/multi-pages.md index 15955baefe..0824df8667 100644 --- a/docs/src/multi-pages.md +++ b/docs/src/multi-pages.md @@ -209,7 +209,7 @@ print(new_page.title()) ```csharp // Get page after a specific action (e.g. clicking a link) -var newPage = await context.RunAndWaitForEventAsync(BrowserContextEvent.Page, async () => +var newPage = await context.RunAndWaitForPageAsync(async () => { await page.ClickAsync("a[target='_blank']"); }); @@ -311,7 +311,7 @@ print(popup.title()) ```csharp // Get popup after a specific action (e.g., click) -var newPage = await page.RunAndWaitForEventAsync(PageEvent.Popup, async () => +var newPage = await page.RunAndWaitForPopupAsync(async () => { await page.ClickAsync("#open"); }); diff --git a/docs/src/navigations.md b/docs/src/navigations.md index 3a629a1a32..8ac7931d84 100644 --- a/docs/src/navigations.md +++ b/docs/src/navigations.md @@ -434,7 +434,7 @@ popup.wait_for_load_state("load") ``` ```csharp -var popup = await page.RunAndWaitForEventAsync(PageEvent.Popup, async () => +var popup = await page.RunAndWaitForPopupAsync(async () => { await page.ClickAsync("a[target='_blank']"); // Opens popup }); diff --git a/docs/src/verification.md b/docs/src/verification.md index bec72a1c02..bee83c9889 100644 --- a/docs/src/verification.md +++ b/docs/src/verification.md @@ -244,7 +244,7 @@ popup = popup_info.value ``` ```csharp -var popup = await page.RunAndWaitForEventAsync(PageEvent.Popup, async () => +var popup = await page.RunAndWaitForPopupAsync(async () => { await page.ClickAsync("#open"); }); diff --git a/utils/doclint/generateDotnetApi.js b/utils/doclint/generateDotnetApi.js index a121e1643d..522cf07681 100644 --- a/utils/doclint/generateDotnetApi.js +++ b/utils/doclint/generateDotnetApi.js @@ -58,10 +58,11 @@ const documentation = parseApi(path.join(PROJECT_DIR, 'docs', 'src', 'api')); documentation.filterForLanguage('csharp'); documentation.setLinkRenderer(item => { + const asyncSuffix = item.member && item.member.async ? 'Async' : ''; if (item.clazz) return ``; else if (item.member) - return ``; + return ``; else if (item.option) return ``; else if (item.param) @@ -137,8 +138,11 @@ function renderClass(clazz) { return; const body = []; - for (const member of clazz.membersArray) + for (const member of clazz.membersArray) { + if (member.alias.startsWith('RunAnd')) + renderMember(member, clazz, { trimRunAndPrefix: true }, body); renderMember(member, clazz, {}, body); + } writeFile( 'public partial interface', @@ -169,8 +173,11 @@ function renderBaseClass(clazz) { return; const body = []; - for (const member of methodsWithOptions) + for (const member of methodsWithOptions) { + if (member.alias.startsWith('RunAnd')) + renderMethod(member, clazz, toMemberName(member), { mode: 'base', nodocs: true, public: true, trimRunAndPrefix: true }, body); renderMethod(member, clazz, toMemberName(member), { mode: 'base', nodocs: true, public: true }, body); + } writeFile( 'internal partial class', @@ -269,16 +276,14 @@ function toArgumentName(name) { /** * @param {Documentation.Member} member - * @param {{ omitAsync?: boolean; }=} options */ -function toMemberName(member, options) { +function toMemberName(member, makeAsync = false) { const assumedName = toTitleCase(member.alias || member.name); if (member.kind === 'interface') return `I${assumedName}`; - const omitAsync = options && options.omitAsync; - if (!omitAsync && member.kind === 'method' && member.async && !assumedName.endsWith('Async')) - return `${assumedName}Async`; - if (omitAsync && assumedName.endsWith('Async')) + if (makeAsync && member.async) + return assumedName + 'Async'; + if (!makeAsync && assumedName.endsWith('Async')) return assumedName.substring(0, assumedName.length - 'Async'.length); return assumedName; } @@ -317,13 +322,13 @@ function renderConstructors(name, type, out) { * * @param {Documentation.Member} member * @param {Documentation.Class|Documentation.Type} parent - * @param {{nojson?: boolean}} options + * @param {{nojson?: boolean, trimRunAndPrefix?: boolean}} options * @param {string[]} out */ function renderMember(member, parent, options, out) { let name = toMemberName(member); if (member.kind === 'method') { - renderMethod(member, parent, name, { mode: 'options' }, out); + renderMethod(member, parent, name, { mode: 'options', trimRunAndPrefix: options.trimRunAndPrefix }, out); return; } @@ -477,23 +482,24 @@ function generateEnumNameIfApplicable(type) { * nodocs?: boolean, * abstract?: boolean, * public?: boolean, + * trimRunAndPrefix?: boolean, * }} options * @param {string[]} out */ function renderMethod(member, parent, name, options, out) { - // These are hard-coded in C#. - if (name.includes('WaitForEventAsync')) - return; out.push(''); + if (options.trimRunAndPrefix) + name = name.substring('RunAnd'.length); + /** * @param {Documentation.Type} type * @returns */ function resolveType(type) { return translateType(type, parent, (t) => { - let newName = `${parent.name}${toMemberName(member, { omitAsync: true })}Result`; - documentedResults.set(newName, `Result of calling .`); + let newName = `${parent.name}${toMemberName(member)}Result`; + documentedResults.set(newName, `Result of calling .`); return newName; }); } @@ -595,9 +601,12 @@ function renderMethod(member, parent, name, options, out) { * @param {Documentation.Member} arg */ function processArg(arg) { + if (options.trimRunAndPrefix && arg.name === 'action') + return; + if (arg.name === 'options') { if (options.mode === 'options' || options.mode === 'base') { - const optionsType = member.clazz.name + toMemberName(member, { omitAsync: true }) + 'Options'; + const optionsType = member.clazz.name + name + 'Options'; optionTypes.set(optionsType, arg.type); args.push(`${optionsType} options = default`); argTypeMap.set(`${optionsType} options = default`, 'options'); @@ -661,6 +670,12 @@ function renderMethod(member, parent, name, options, out) { pushArg(argType, argName, arg); } + let modifiers = ''; + if (options.abstract) + modifiers = 'protected abstract '; + if (options.public) + modifiers = 'public '; + member.argsArray .sort((a, b) => b.alias === 'options' ? -1 : 0) //move options to the back to the arguments list .forEach(processArg); @@ -670,6 +685,8 @@ function renderMethod(member, parent, name, options, out) { // Generate options -> named transition. const tokens = []; for (const arg of member.argsArray) { + if (arg.name === 'action' && options.trimRunAndPrefix) + continue; if (arg.name !== 'options') { tokens.push(toArgumentName(arg.name)); continue; @@ -689,23 +706,17 @@ function renderMethod(member, parent, name, options, out) { } body = ` { - options ??= new ${member.clazz.name}${toMemberName(member, { omitAsync: true })}Options(); - return ${name}(${tokens.join(', ')}); + options ??= new ${member.clazz.name}${name}Options(); + return ${toAsync(name, member.async)}(${tokens.join(', ')}); }`; } - let modifiers = ''; - if (options.abstract) - modifiers = 'protected abstract '; - if (options.public) - modifiers = 'public '; - if (!explodedArgs.length) { if (!options.nodocs) { out.push(...XmlDoc.renderXmlDoc(member.spec, maxDocumentationColumnWidth)); paramDocs.forEach((value, i) => printArgDoc(i, value, out)); } - out.push(`${modifiers}${type} ${name}(${args.join(', ')})${body}`); + out.push(`${modifiers}${type} ${toAsync(name, member.async)}(${args.join(', ')})${body}`); } else { let containsOptionalExplodedArgs = false; explodedArgs.forEach((explodedArg, argIndex) => { @@ -727,7 +738,7 @@ function renderMethod(member, parent, name, options, out) { overloadedArgs.push(arg); } } - out.push(`${modifiers}${type} ${name}(${overloadedArgs.join(', ')})${body}`); + out.push(`${modifiers}${type} ${toAsync(name, member.async)}(${overloadedArgs.join(', ')})${body}`); if (argIndex < explodedArgs.length - 1) out.push(''); // output a special blank line }); @@ -914,3 +925,15 @@ function printArgDoc(name, value, out) { function toOverloadSuffix(typeName) { return toTitleCase(typeName.replace(/[<].*[>]/, '').replace(/[^a-zA-Z]/g, '')); } + +/** + * @param {string} name + * @param {boolean} convert + */ +function toAsync(name, convert) { + if (!convert) + return name; + if (name.includes('<')) + return name.replace('<', 'Async<'); + return name + 'Async'; +}