diff --git a/docs/src/api/class-page.md b/docs/src/api/class-page.md index 4e51e4e637..3be771c119 100644 --- a/docs/src/api/class-page.md +++ b/docs/src/api/class-page.md @@ -73,6 +73,23 @@ with sync_playwright() as playwright: run(playwright) ``` +```csharp +using Microsoft.Playwright; +using System.Threading.Tasks; + +class PageExamples +{ + public static async Task Run() + { + using var playwright = await Playwright.CreateAsync(); + await using var browser = await playwright.Webkit.LaunchAsync(); + var page = await browser.NewPageAsync(); + await page.GoToAsync("https://www.theverge.com"); + await page.ScreenshotAsync("theverge.png"); + } +} +``` + The Page class emits various events (described below) which can be handled using any of Node's native [`EventEmitter`](https://nodejs.org/api/events.html#events_class_eventemitter) methods, such as `on`, `once` or `removeListener`. @@ -91,6 +108,10 @@ page.onLoad(p -> System.out.println("Page loaded!")); page.once("load", lambda: print("page loaded!")) ``` +```csharp +page.Load += (_, _) => { Console.WriteLine("Page loaded!"); }; +``` + To unsubscribe from events use the `removeListener` method: ```js @@ -119,6 +140,16 @@ page.on("request", log_request) page.remove_listener("request", log_request) ``` +```csharp +void PageLoadHandler(object _, IPage p) { + Console.WriteLine("Page loaded!"); +}; + +page.Load += PageLoadHandler; +// Do some work... +page.Load -= PageLoadHandler; +``` + ## event: Page.close - argument: <[Page]> @@ -170,6 +201,16 @@ page.on("console", print_args) page.evaluate("console.log('hello', 5, {foo: 'bar'})") ``` +```csharp +page.Console += async (_, msg) => +{ + foreach (var arg in msg.Args) + Console.WriteLine(await arg.JsonValueAsync()); +}; + +await page.EvaluateAsync("console.log('hello', 5, { foo: 'bar' })"); +``` + ## event: Page.crash - argument: <[Page]> @@ -220,6 +261,17 @@ except Error as e: # when the page crashes, exception message contains "crash". ``` +```csharp +try { + // Crash might happen during a click. + await page.ClickAsync("button"); + // Or while waiting for an event. + await page.WaitForPopupAsync(() -> {}); +} catch (PlaywrightException e) { + // When the page crashes, exception message contains "crash". +} +``` + ## event: Page.dialog - argument: <[Dialog]> @@ -269,6 +321,13 @@ page.onFileChooser(fileChooser -> { page.on("filechooser", lambda file_chooser: file_chooser.set_files("/tmp/myfile.pdf")) ``` +```csharp +page.FileChooser += (_, fileChooser) => +{ + fileChooser.SetFilesAsync(@"C:\temp\myfile.pdf"); +}; +``` + ## event: Page.frameAttached - argument: <[Frame]> @@ -337,6 +396,12 @@ popup = page_info.value print(popup.evaluate("location.href")) ``` +```csharp +var popupTask = page.WaitForPopupAsync(); +await Task.WhenAll(popupTask, page.EvaluateAsync("() => window.open('https://microsoft.com')")); +Console.WriteLine(await popupTask.Result.EvaluateAsync("location.href")); +``` + :::note Use [`method: Page.waitForLoadState`] to wait until the page gets to a particular state (you should not need it in most cases). @@ -422,6 +487,10 @@ await page.add_init_script(path="./preload.js") page.add_init_script(path="./preload.js") ``` +```csharp +await page.AddInitScriptAsync(scriptPath: "./preload.js"); +``` + :::note The order of evaluation of multiple scripts installed via [`method: BrowserContext.addInitScript`] and [`method: Page.addInitScript`] is not defined. @@ -674,6 +743,10 @@ await page.dispatch_event("button#submit", "click") page.dispatch_event("button#submit", "click") ``` +```csharp +await page.DispatchEventAsync("button#submit", "click"); +``` + Under the hood, it creates an instance of an event based on the given [`param: type`], initializes it with [`param: eventInit`] properties and dispatches it on the element. Events are `composed`, `cancelable` and bubble by default. @@ -716,6 +789,11 @@ data_transfer = page.evaluate_handle("new DataTransfer()") page.dispatch_event("#source", "dragstart", { "dataTransfer": data_transfer }) ``` +```csharp +var dataTransfer = await page.EvaluateHandleAsync("() => new DataTransfer()"); +await page.DispatchEventAsync("#source", "dragstart", new { dataTransfer }); +``` + ### param: Page.dispatchEvent.selector = %%-input-selector-%% ### param: Page.dispatchEvent.type @@ -810,6 +888,25 @@ page.evaluate("matchMedia('print').matches") # → False ``` +```csharp +await page.EvaluateAsync("() => matchMedia('screen').matches"); +// → true +await page.EvaluateAsync("() => matchMedia('print').matches"); +// → false + +await page.EmulateMediaAsync(Media.Print); +await page.EvaluateAsync("() => matchMedia('screen').matches"); +// → false +await page.EvaluateAsync("() => matchMedia('print').matches"); +// → true + +await page.EmulateMediaAsync(Media.Screen); +await page.EvaluateAsync("() => matchMedia('screen').matches"); +// → true +await page.EvaluateAsync("() => matchMedia('print').matches"); +// → false +``` + ```js await page.emulateMedia({ colorScheme: 'dark' }); await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches); @@ -849,6 +946,16 @@ page.evaluate("matchMedia('(prefers-color-scheme: light)').matches") page.evaluate("matchMedia('(prefers-color-scheme: no-preference)').matches") ``` +```csharp +await page.EmulateMediaAsync(colorScheme: ColorScheme.Dark); +await page.EvaluateAsync("matchMedia('(prefers-color-scheme: dark)').matches"); +// → true +await page.EvaluateAsync("matchMedia('(prefers-color-scheme: light)').matches"); +// → false +await page.EvaluateAsync("matchMedia('(prefers-color-scheme: no-preference)').matches"); +// → false +``` + ### option: Page.emulateMedia.media - `media` > @@ -900,6 +1007,12 @@ preload_href = page.eval_on_selector("link[rel=preload]", "el => el.href") html = page.eval_on_selector(".main-container", "(e, suffix) => e.outer_html + suffix", "hello") ``` +```csharp +var searchValue = await page.EvalOnSelectorAsync("#search", "el => el.value"); +var preloadHref = await page.EvalOnSelectorAsync("link[rel=preload]", "el => el.href"); +var html = await page.EvalOnSelectorAsync(".main-container", "(e, suffix) => e.outerHTML + suffix", "hello"); +``` + Shortcut for main frame's [`method: Frame.evalOnSelector`]. ### param: Page.evalOnSelector.selector = %%-query-selector-%% @@ -941,6 +1054,10 @@ div_counts = await page.eval_on_selector_all("div", "(divs, min) => divs.length div_counts = page.eval_on_selector_all("div", "(divs, min) => divs.length >= min", 10) ``` +```csharp +var divsCount = await page.EvalOnSelectorAllAsync("div", "(divs, min) => divs.length >= min", 10); +``` + ### param: Page.evalOnSelectorAll.selector = %%-query-selector-%% ### param: Page.evalOnSelectorAll.expression = %%-evaluate-expression-%% @@ -988,6 +1105,11 @@ result = page.evaluate("([x, y]) => Promise.resolve(x * y)", [7, 8]) print(result) # prints "56" ``` +```csharp +var result = await page.EvaluateAsync("([x, y]) => Promise.resolve(x * y)", new[] { 7, 8 }); +Console.WriteLine(result); +``` + A string can also be passed in instead of a function: ```js @@ -1012,6 +1134,10 @@ x = 10 print(page.evaluate(f"1 + {x}")) # prints "11" ``` +```csharp +Console.WriteLine(await page.EvaluateAsync("1 + 2")); // prints "3" +``` + [ElementHandle] instances can be passed as an argument to the [`method: Page.evaluate`]: ```js @@ -1038,6 +1164,12 @@ html = page.evaluate("([body, suffix]) => body.innerHTML + suffix", [body_handle body_handle.dispose() ``` +```csharp +var bodyHandle = await page.QuerySelectorAsync("body"); +var html = await page.EvaluateAsync("([body, suffix]) => body.innerHTML + suffix", new object [] { bodyHandle, "hello" }); +await bodyHandle.DisposeAsync(); +``` + Shortcut for main frame's [`method: Frame.evaluate`]. ### param: Page.evaluate.expression = %%-evaluate-expression-%% @@ -1077,6 +1209,11 @@ a_window_handle = page.evaluate_handle("Promise.resolve(window)") a_window_handle # handle for the window object. ``` +```csharp +// Handle for the window object. +var aWindowHandle = await page.EvaluateHandleAsync("() => Promise.resolve(window)"); +``` + A string can also be passed in instead of a function: ```js @@ -1095,6 +1232,10 @@ a_handle = await page.evaluate_handle("document") # handle for the "document" a_handle = page.evaluate_handle("document") # handle for the "document" ``` +```csharp +var docHandle = await page.EvalueHandleAsync("document"); // Handle for the `document` +``` + [JSHandle] instances can be passed as an argument to the [`method: Page.evaluateHandle`]: ```js @@ -1125,6 +1266,13 @@ print(result_handle.json_value()) result_handle.dispose() ``` +```csharp +var handle = await page.EvaluateHandleAsync("() => document.body"); +var resultHandle = await page.EvaluateHandleAsync("([body, suffix]) => body.innerHTML + suffix", new object[] { handle, "hello" }); +Console.WriteLine(await resultHandle.JsonValueAsync()); +await resultHandle.DisposeAsync(); +``` + ### param: Page.evaluateHandle.expression = %%-evaluate-expression-%% ### param: Page.evaluateHandle.arg @@ -1245,6 +1393,32 @@ with sync_playwright() as playwright: run(playwright) ``` +```csharp +using Microsoft.Playwright; +using System.Threading.Tasks; + +class PageExamples +{ + public static async Task Main() + { + using var playwright = await Playwright.CreateAsync(); + await using var browser = await playwright.Webkit.LaunchAsync(headless: false); + var page = await browser.NewPageAsync(); + + await page.ExposeBindingAsync("pageUrl", (source) => source.Page.Url); + await page.SetContentAsync("\n" + + "\n" + + "
"); + + await page.ClickAsync("button"); + } +} +``` + An example of passing an element handle: ```js @@ -1302,6 +1476,23 @@ page.set_content(""" """) ``` +```csharp +var result = new TaskCompletionSource(); +await page.ExposeBindingAsync("clicked", async (BindingSource _, IJSHandle t) => +{ + return result.TrySetResult(await t.AsElement.TextContentAsync()); +}); + +await page.SetContentAsync("\n" + + "
Click me
\n" + + "
Or click me
\n"); + +await page.ClickAsync("div"); +Console.WriteLine(await result.Task); +``` + ### param: Page.exposeBinding.name - `name` <[string]> @@ -1455,6 +1646,42 @@ with sync_playwright() as playwright: run(playwright) ``` +```csharp +using Microsoft.Playwright; +using System; +using System.Security.Cryptography; +using System.Threading.Tasks; + +class PageExamples +{ + public static async Task Main() + { + using var playwright = await Playwright.CreateAsync(); + await using var browser = await playwright.Webkit.LaunchAsync(headless: false); + var page = await browser.NewPageAsync(); + + // NOTE: md5 is inherently insecure, and we strongly discourage using + // this in production in any shape or form + await page.ExposeFunctionAsync("sha1", (string input) => + { + return Convert.ToBase64String( + MD5.Create().ComputeHash(System.Text.Encoding.UTF8.GetBytes(input))); + }); + + await page.SetContentAsync("\n" + + "\n" + + "
"); + + await page.ClickAsync("button"); + Console.WriteLine(await page.TextContentAsync("div")); + } +} +``` + ### param: Page.exposeFunction.name - `name` <[string]> @@ -1514,6 +1741,10 @@ Frame frame = page.frame("frame-name"); frame = page.frame(name="frame-name") ``` +```csharp +var frame = page.Frame("frame-name"); +``` + ```js const frame = page.frame({ url: /.*domain.*/ }); ``` @@ -1526,6 +1757,10 @@ Frame frame = page.frameByUrl(Pattern.compile(".*domain.*"); frame = page.frame(url=r".*domain.*") ``` +```csharp +var frame = page.FrameByUrl(".*domain.*"); +``` + ### param: Page.frame.frameSelector * langs: js - `frameSelector` <[string]|[Object]> @@ -1818,6 +2053,12 @@ page.emulate_media(media="screen") page.pdf(path="page.pdf") ``` +```csharp +// Generates a PDF with 'screen' media type +await page.EmulateMediaAsync(Media.Screen); +await page.PdfAsync("page.pdf"); +``` + The [`option: width`], [`option: height`], and [`option: margin`] options accept values labeled with units. Unlabeled values are treated as pixels. @@ -2022,6 +2263,18 @@ page.screenshot(path="o.png") browser.close() ``` +```csharp +await using var browser = await playwright.Webkit.LaunchAsync(headless: false); +var page = await browser.NewPageAsync(); +await page.GoToAsync("https://keycode.info"); +await page.PressAsync("body", "A"); +await page.ScreenshotAsync("A.png"); +await page.PressAsync("body", "ArrowLeft"); +await page.ScreenshotAsync("ArrowLeft.png"); +await page.PressAsync("body", "Shift+O"); +await page.ScreenshotAsync("O.png"); +``` + ### param: Page.press.selector = %%-input-selector-%% ### param: Page.press.key @@ -2114,6 +2367,13 @@ page.goto("https://example.com") browser.close() ``` +```csharp +await using var browser = await playwright.Webkit.LaunchAsync(); +var page = await browser.NewPageAsync(); +await page.RouteAsync("**/*.{png,jpg,jpeg}", async r => await r.AbortAsync()); +await page.GoToAsync("https://www.microsoft.com"); +``` + or the same snippet using a regex pattern instead: ```js @@ -2144,6 +2404,13 @@ page.goto("https://example.com") browser.close() ``` +```csharp +await using var browser = await playwright.Webkit.LaunchAsync(); +var page = await browser.NewPageAsync(); +await page.RouteAsync(new Regex("(\\.png$)|(\\.jpg$)"), async r => await r.AbortAsync()); +await page.GoToAsync("https://www.microsoft.com"); +``` + It is possible to examine the request to decide the route action. For example, mocking all requests that contain some post data, and leaving all other requests as is: ```js @@ -2182,6 +2449,16 @@ def handle_route(route): page.route("/api/**", handle_route) ``` +```csharp +await page.RouteAsync("/api/**", async r => +{ + if (r.Request.PostData.Contains("my-string")) + await r.FulfillAsync(body: "mocked-data"); + else + await r.ResumeAsync(); +}); +``` + Page routes take precedence over browser context routes (set up with [`method: BrowserContext.route`]) when request matches both handlers. @@ -2300,6 +2577,15 @@ page.select_option("select#colors", label="blue") page.select_option("select#colors", value=["red", "green", "blue"]) ``` +```csharp +// single selection matching the value +await page.SelectOptionAsync("select#colors", new[] { "blue" }); +// single selection matching both the value and the label +await page.SelectOptionAsync("select#colors", new[] { new SelectOptionValue() { Label = "blue" } }); +// multiple +await page.SelectOptionAsync("select#colors", new[] { "red", "green", "blue" }); +``` + Shortcut for main frame's [`method: Frame.selectOption`]. ### param: Page.selectOption.selector = %%-input-selector-%% @@ -2419,6 +2705,12 @@ page.set_viewport_size({"width": 640, "height": 480}) page.goto("https://example.com") ``` +```csharp +var page = await browser.NewPageAsync(); +await page.SetViewportSizeAsync(640, 480); +await page.GoToAsync("https://www.microsoft.com"); +``` + ### param: Page.setViewportSize.viewportSize * langs: js, python - `viewportSize` <[Object]> @@ -2513,6 +2805,11 @@ page.type("#mytextarea", "hello") # types instantly page.type("#mytextarea", "world", delay=100) # types slower, like a user ``` +```csharp +await page.TypeAsync("#mytextarea", "hello"); // types instantly +await page.TypeAsync("#mytextarea", "world"); // types slower, like a user +``` + Shortcut for main frame's [`method: Frame.type`]. ### param: Page.type.selector = %%-input-selector-%% @@ -2666,6 +2963,12 @@ with page.expect_event("framenavigated") as event_info: frame = event_info.value ``` +```csharp +var waitTask = page.WaitForEventAsync(PageEvent.FrameNavigated); +await Task.WhenAll(waitTask, page.ClickAsync("button")); +var frame = waitTask.Result; +``` + ### param: Page.waitForEvent.event = %%-wait-for-event-event-%% ### param: Page.waitForEvent.optionsOrPredicate @@ -2763,6 +3066,23 @@ with sync_playwright() as playwright: run(playwright) ``` +```csharp +using Microsoft.Playwright; +using System.Threading.Tasks; + +class FrameExamples +{ + public static async Task WaitForFunction() + { + using var playwright = await Playwright.CreateAsync(); + await using var browser = await playwright.Webkit.LaunchAsync(); + var page = await browser.NewPageAsync(); + await page.SetViewportSizeAsync(50, 50); + await page.MainFrame.WaitForFunctionAsync("window.innerWidth < 100"); + } +} +``` + To pass an argument to the predicate of [`method: Page.waitForFunction`] function: ```js @@ -2785,6 +3105,11 @@ selector = ".foo" page.wait_for_function("selector => !!document.querySelector(selector)", selector) ``` +```csharp +var selector = ".foo"; +await page.WaitForFunctionAsync("selector => !!document.querySelector(selector)", selector); +``` + Shortcut for main frame's [`method: Frame.waitForFunction`]. ### param: Page.waitForFunction.expression = %%-evaluate-expression-%% @@ -2827,6 +3152,11 @@ page.click("button") # click triggers navigation. page.wait_for_load_state() # the promise resolves after "load" event. ``` +```csharp +await page.ClickAsync("button"); // Click triggers navigation. +await page.WaitForLoadStateAsync(); // The promise resolves after 'load' event. +``` + ```js const [popup] = await Promise.all([ page.waitForEvent('popup'), @@ -2862,6 +3192,13 @@ popup.wait_for_load_state("domcontentloaded") print(popup.title()) # popup is ready to use. ``` +```csharp +var popupTask = page.WaitForPopupAsync(); +await Task.WhenAll(popupTask, page.ClickAsync("button")); // click triggers the popup/ +await popupTask.Result.WaitForLoadStateAsync(LoadState.DOMContentLoaded); +Console.WriteLine(await popupTask.Result.TitleAsync()); // popup is ready to use. +``` + Shortcut for main frame's [`method: Frame.waitForLoadState`]. ### param: Page.waitForLoadState.state = %%-wait-for-load-state-state-%% @@ -2907,6 +3244,12 @@ with page.expect_navigation(): # Resolves after navigation has finished ``` +```csharp +await Task.WhenAll(page.WaitForNavigationAsync(), + frame.ClickAsync("a.delayed-navigation")); // clicking the link will indirectly cause a navigation +// The method continues after navigation has finished +``` + :::note Usage of the [History API](https://developer.mozilla.org/en-US/docs/Web/API/History_API) to change the URL is considered a navigation. @@ -2998,6 +3341,16 @@ with page.expect_request(lambda request: request.url == "http://example.com" and second_request = second.value ``` +```csharp +// Waits for the next response with the specified url +await Task.WhenAll(page.WaitForRequestAsync("https://example.com/resource"), + page.ClickAsync("button.triggers-request")); + +// Waits for the next request matching some conditions +await Task.WhenAll(page.WaitForRequestAsync(r => "https://example.com".Equals(r.Url) && "GET" == r.Method), + page.ClickAsync("button.triggers-request")); +``` + ```js await page.waitForRequest(request => request.url().searchParams.get('foo') === 'bar' && request.url().searchParams.get('foo2') === 'bar2'); ``` @@ -3085,6 +3438,16 @@ response = response_info.value return response.ok ``` +```csharp +// Waits for the next response with the specified url +await Task.WhenAll(page.WaitForResponseAsync("https://example.com/resource"), + page.ClickAsync("button.triggers-response")); + +// Waits for the next response matching some conditions +await Task.WhenAll(page.WaitForResponseAsync(r => "https://example.com".Equals(r.Url) && r.Status == 200), + page.ClickAsync("button.triggers-response")); +``` + ### param: Page.waitForResponse.urlOrPredicate - `urlOrPredicate` <[string]|[RegExp]|[function]\([Response]\):[boolean]> @@ -3187,6 +3550,31 @@ with sync_playwright() as playwright: run(playwright) ``` +```csharp +using Microsoft.Playwright; +using System; +using System.Threading.Tasks; + +class FrameExamples +{ + public static async Task Images() + { + using var playwright = await Playwright.CreateAsync(); + await using var browser = await playwright.Chromium.LaunchAsync(); + var page = await browser.NewPageAsync(); + + foreach (var currentUrl in new[] { "https://www.google.com", "https://bbc.com" }) + { + await page.GoToAsync(currentUrl); + var element = await page.WaitForSelectorAsync("img"); + Console.WriteLine($"Loaded image: {await element.GetAttributeAsync("src")}"); + } + + await browser.CloseAsync(); + } +} +``` + ### param: Page.waitForSelector.selector = %%-query-selector-%% ### option: Page.waitForSelector.state = %%-wait-for-selector-state-%% @@ -3220,6 +3608,11 @@ await page.wait_for_timeout(1000) page.wait_for_timeout(1000) ``` +```csharp +// Wait for 1 second +await page.WaitForTimeoutAsync(1000); +``` + Shortcut for main frame's [`method: Frame.waitForTimeout`]. ### param: Page.waitForTimeout.timeout @@ -3251,6 +3644,11 @@ page.click("a.delayed-navigation") # clicking the link will indirectly cause a n page.wait_for_url("**/target.html") ``` +```csharp +await page.ClickAsync("a.delayed-navigation"); // clicking the link will indirectly cause a navigation +await page.WaitForURLAsync("**/target.html"); +``` + Shortcut for main frame's [`method: Frame.waitForURL`]. ### param: Page.waitForURL.url = %%-wait-for-navigation-url-%%