diff --git a/README.md b/README.md index 3cd80a7ea3..e248716e9d 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 129.0.6668.29 | ✅ | ✅ | ✅ | +| Chromium 130.0.6723.31 | ✅ | ✅ | ✅ | | WebKit 18.0 | ✅ | ✅ | ✅ | -| Firefox 130.0 | ✅ | ✅ | ✅ | +| Firefox 131.0 | ✅ | ✅ | ✅ | Playwright for .NET is the official language port of [Playwright](https://playwright.dev), the library to automate [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable** and **fast**. diff --git a/src/Common/Version.props b/src/Common/Version.props index 42dce62d33..67d204963d 100644 --- a/src/Common/Version.props +++ b/src/Common/Version.props @@ -2,7 +2,7 @@ 1.47.0 $(AssemblyVersion) - 1.47.0-beta-1726138322000 + 1.48.0 $(AssemblyVersion) $(AssemblyVersion) true diff --git a/src/Playwright.Tests.TestServer/SimpleServer.cs b/src/Playwright.Tests.TestServer/SimpleServer.cs index 56108cb2b2..e3e53c5e76 100644 --- a/src/Playwright.Tests.TestServer/SimpleServer.cs +++ b/src/Playwright.Tests.TestServer/SimpleServer.cs @@ -49,21 +49,16 @@ namespace Microsoft.Playwright.Tests.TestServer; public class SimpleServer { - const int MaxMessageSize = 256 * 1024; - private readonly IDictionary> _requestWaits; - private readonly IList> _waitForWebSocketConnectionRequestsWaits; + private readonly ConcurrentBag> _waitForWebSocketConnectionRequestsWaits; private readonly IDictionary> _routes; private readonly IDictionary _auths; private readonly IDictionary _csp; private readonly IList _gzipRoutes; private readonly string _contentRoot; - private ArraySegment _onWebSocketConnectionData; private readonly IWebHost _webHost; - private static int _counter; - private readonly Dictionary _clients = new(); public int Port { get; } public string Prefix { get; } @@ -92,6 +87,13 @@ public SimpleServer(int port, string contentRoot, bool isHttps) _gzipRoutes = new List(); _contentRoot = contentRoot; + var loggerFactory = LoggerFactory.Create(builder => + { + builder + .AddConsole() + .SetMinimumLevel(LogLevel.Debug); // or LogLevel.Debug + }); + _webHost = new WebHostBuilder() .ConfigureLogging(logging => { @@ -108,26 +110,19 @@ public SimpleServer(int port, string contentRoot, bool isHttps) var currentContext = typeof(TestExecutionContext).GetField("AsyncLocalCurrentContext", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null) as AsyncLocal; currentContext.Value = currentExecutionContext; } - if (context.Request.Path == "/ws") + if (context.WebSockets.IsWebSocketRequest && context.Request.Path == "/ws") { - if (context.WebSockets.IsWebSocketRequest) + var webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); + var advancedWebSocket = new WebSocketWithEvents(webSocket); + if (_onWebSocketConnectionData != null) { - var webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false); - foreach (var wait in _waitForWebSocketConnectionRequestsWaits) - { - _waitForWebSocketConnectionRequestsWaits.Remove(wait); - await wait(webSocket, context).ConfigureAwait(false); - } - if (_onWebSocketConnectionData != null) - { - await webSocket.SendAsync(_onWebSocketConnectionData, WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false); - } - await ReceiveLoopAsync(webSocket, context.Request.Headers["User-Agent"].ToString().Contains("Firefox"), CancellationToken.None).ConfigureAwait(false); + await webSocket.SendAsync(_onWebSocketConnectionData, WebSocketMessageType.Text, true, CancellationToken.None).ConfigureAwait(false); } - else if (!context.Response.HasStarted) + foreach (var wait in _waitForWebSocketConnectionRequestsWaits) { - context.Response.StatusCode = 400; + await wait(advancedWebSocket, context).ConfigureAwait(false); } + await advancedWebSocket.RunReceiveLoop().ConfigureAwait(false); return; } @@ -267,6 +262,7 @@ public void Reset() _requestWaits.Clear(); _gzipRoutes.Clear(); _onWebSocketConnectionData = null; + _waitForWebSocketConnectionRequestsWaits.Clear(); } public void EnableGzip(string path) => _gzipRoutes.Add(path); @@ -303,18 +299,28 @@ public async Task WaitForRequest(string path, Func selecto public Task WaitForRequest(string path) => WaitForRequest(path, _ => true); + public Task WaitForWebSocketAsync() + { + var tcs = new TaskCompletionSource(); + OnceWebSocketConnection((ws, _) => { + tcs.SetResult(ws); + return Task.CompletedTask; + }); + return tcs.Task; + } + public async Task<(WebSocket, HttpRequest)> WaitForWebSocketConnectionRequest() { var taskCompletion = new TaskCompletionSource<(WebSocket, HttpRequest)>(); - OnceWebSocketConnection((WebSocket ws, HttpContext context) => + OnceWebSocketConnection((WebSocketWithEvents ws, HttpContext context) => { - taskCompletion.SetResult((ws, context.Request)); + taskCompletion.SetResult((ws._ws, context.Request)); return Task.CompletedTask; }); return await taskCompletion.Task.ConfigureAwait(false); } - public void OnceWebSocketConnection(Func handler) + public void OnceWebSocketConnection(Func handler) { _waitForWebSocketConnectionRequestsWaits.Add(handler); } @@ -332,73 +338,4 @@ private static bool Authenticate(string username, string password, HttpContext c } return false; } - - private async Task ReceiveLoopAsync(WebSocket webSocket, bool sendCloseMessage, CancellationToken token) - { - int connectionId = NextConnectionId(); - _clients.Add(connectionId, webSocket); - - byte[] buffer = new byte[MaxMessageSize]; - - try - { - while (true) - { - var result = await webSocket.ReceiveAsync(new(buffer), token).ConfigureAwait(false); - - if (result.MessageType == WebSocketMessageType.Close) - { - if (sendCloseMessage) - { - await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Close", CancellationToken.None).ConfigureAwait(false); - } - break; - } - - var data = await ReadFrames(result, webSocket, buffer, token).ConfigureAwait(false); - - if (data.Count == 0) - { - break; - } - } - } - finally - { - _clients.Remove(connectionId); - } - } - - private async Task> ReadFrames(WebSocketReceiveResult result, WebSocket webSocket, byte[] buffer, CancellationToken token) - { - int count = result.Count; - - while (!result.EndOfMessage) - { - if (count >= MaxMessageSize) - { - string closeMessage = $"Maximum message size: {MaxMessageSize} bytes."; - await webSocket.CloseAsync(WebSocketCloseStatus.MessageTooBig, closeMessage, token).ConfigureAwait(false); - return new(); - } - - result = await webSocket.ReceiveAsync(new(buffer, count, MaxMessageSize - count), token).ConfigureAwait(false); - count += result.Count; - - } - return new(buffer, 0, count); - } - - - private static int NextConnectionId() - { - int id = Interlocked.Increment(ref _counter); - - if (id == int.MaxValue) - { - throw new("connection id limit reached: " + id); - } - - return id; - } } diff --git a/src/Playwright.Tests.TestServer/WebSocketWithEvents.cs b/src/Playwright.Tests.TestServer/WebSocketWithEvents.cs new file mode 100644 index 0000000000..2ccc4ff958 --- /dev/null +++ b/src/Playwright.Tests.TestServer/WebSocketWithEvents.cs @@ -0,0 +1,112 @@ +/* + * MIT License + * + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System; +using System.Net.WebSockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.Playwright.Tests.TestServer; + +public class WebSocketWithEvents : IDisposable +{ + public readonly WebSocket _ws; + private readonly CancellationTokenSource _cts = new CancellationTokenSource(); + + public WebSocketWithEvents(WebSocket webSocket) + { + _ws = webSocket; + } + + public event EventHandler MessageReceived; + public event EventHandler<(WebSocketCloseStatus? Code, string Reason)> Closed; + + internal async Task RunReceiveLoop() + { + var buffer = new byte[1024 * 4]; + try + { + while (_ws.State == WebSocketState.Open && !_cts.IsCancellationRequested) + { + var result = await _ws.ReceiveAsync(new ArraySegment(buffer), _cts.Token).ConfigureAwait(false); + Console.WriteLine($"Received {result.Count} bytes messagetype: {result.MessageType}"); + if (result.MessageType == WebSocketMessageType.Text) + { + var message = Encoding.UTF8.GetString(buffer, 0, result.Count); + MessageReceived?.Invoke(this, message); + } + else if (result.MessageType == WebSocketMessageType.Close) + { + await CloseAsync().ConfigureAwait(false); + break; + } + } + } + catch (OperationCanceledException) + { + // Normal cancellation, do nothing + } + finally + { + if (_ws.State == WebSocketState.CloseReceived) + { + Closed?.Invoke(this, (_ws.CloseStatus, _ws.CloseStatusDescription)); + } + await CloseAsync().ConfigureAwait(false); + } + } + + public async Task SendAsync(string message) + { + if (_ws.State != WebSocketState.Open) + throw new InvalidOperationException("WebSocket is not open."); + + var buffer = Encoding.UTF8.GetBytes(message); + await _ws.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, _cts.Token).ConfigureAwait(false); + } + + public async Task CloseAsync(WebSocketCloseStatus closeStatus = WebSocketCloseStatus.NormalClosure, string statusDescription = "Closing") + { + Console.WriteLine(Environment.StackTrace); + if (_ws.State == WebSocketState.Open) + { + try + { + await _ws.CloseAsync(closeStatus, statusDescription, CancellationToken.None).ConfigureAwait(false); + } + catch (WebSocketException) + { + // The WebSocket might have been closed by the server, ignore the exception + } + } + } + + public void Dispose() + { + _cts.Cancel(); + _cts.Dispose(); + _ws.Dispose(); + } +} diff --git a/src/Playwright.Tests/BrowserTypeConnectOverCDPTests.cs b/src/Playwright.Tests/BrowserTypeConnectOverCDPTests.cs index 8ed6d4c62b..34a68d0edc 100644 --- a/src/Playwright.Tests/BrowserTypeConnectOverCDPTests.cs +++ b/src/Playwright.Tests/BrowserTypeConnectOverCDPTests.cs @@ -102,8 +102,8 @@ public async Task ShouldPrintCustomWsCloseError() { Server.OnceWebSocketConnection(async (ws, request) => { - await ws.ReceiveAsync(new byte[1024], CancellationToken.None); - await ws.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "Oh my!", CancellationToken.None); + await ws._ws.ReceiveAsync(new byte[1024], CancellationToken.None); + await ws._ws.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.NormalClosure, "Oh my!", CancellationToken.None); }); var error = await PlaywrightAssert.ThrowsAsync(() => BrowserType.ConnectOverCDPAsync($"ws://localhost:{Server.Port}/ws")); StringAssert.Contains("Browser logs:\n\nOh my!\n", error.Message); diff --git a/src/Playwright.Tests/BrowserTypeConnectTests.cs b/src/Playwright.Tests/BrowserTypeConnectTests.cs index 9c8e57b3a3..d5401eb362 100644 --- a/src/Playwright.Tests/BrowserTypeConnectTests.cs +++ b/src/Playwright.Tests/BrowserTypeConnectTests.cs @@ -608,8 +608,8 @@ public async Task ShouldPrintCustomWsCloseError() { Server.OnceWebSocketConnection(async (webSocket, _) => { - await webSocket.ReceiveAsync(new byte[1], CancellationToken.None); - await webSocket.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.PolicyViolation, "Oh my!", CancellationToken.None); + await webSocket._ws.ReceiveAsync(new byte[1], CancellationToken.None); + await webSocket._ws.CloseAsync(System.Net.WebSockets.WebSocketCloseStatus.PolicyViolation, "Oh my!", CancellationToken.None); }); var error = await PlaywrightAssert.ThrowsAsync(() => BrowserType.ConnectAsync($"ws://localhost:{Server.Port}/ws")); StringAssert.Contains("Oh my!", error.Message); diff --git a/src/Playwright.Tests/Locator/LocatorFrameTests.cs b/src/Playwright.Tests/Locator/LocatorFrameTests.cs index eca164f562..c947d4a2af 100644 --- a/src/Playwright.Tests/Locator/LocatorFrameTests.cs +++ b/src/Playwright.Tests/Locator/LocatorFrameTests.cs @@ -125,8 +125,8 @@ public async Task ShouldWorkForAnd() public async Task ShouldWaitForFrame() { await Page.GotoAsync(Server.EmptyPage); - var error = await Page.FrameLocator("iframe").Locator("span").ClickAsync(new() { Timeout = 1000 }).ContinueWith(t => t.Exception.InnerException); - StringAssert.Contains("waiting for FrameLocator(\"iframe\")", error.Message); + var error = await Page.Locator("body").FrameLocator("iframe").Locator("span").ClickAsync(new() { Timeout = 1000 }).ContinueWith(t => t.Exception.InnerException); + StringAssert.Contains("waiting for Locator(\"body\").Locator(\"iframe\").ContentFrame.Locator(\"span\")", error.Message); } [PlaywrightTest("locator-frame.spec.ts", "should wait for frame 2")] diff --git a/src/Playwright.Tests/PageRequestGCTests.cs b/src/Playwright.Tests/PageRequestGCTests.cs new file mode 100644 index 0000000000..95cf22d79a --- /dev/null +++ b/src/Playwright.Tests/PageRequestGCTests.cs @@ -0,0 +1,53 @@ +/* + * MIT License + * + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +namespace Microsoft.Playwright.Tests; + +public class PageRequestGCTests : PageTestEx +{ + [PlaywrightTest("page-gc.spec.ts", "should work")] + public async Task ShouldWork() + { + await Page.EvaluateAsync(@"() => { + globalThis.objectToDestroy = { hello: 'world' }; + globalThis.weakRef = new WeakRef(globalThis.objectToDestroy); + }"); + + await Page.RequestGCAsync(); + Assert.AreEqual(new Dictionary + { + ["hello"] = "world", + }, await Page.EvaluateAsync("() => globalThis.weakRef.deref()")); + + await Page.RequestGCAsync(); + Assert.AreEqual(new Dictionary + { + ["hello"] = "world", + }, await Page.EvaluateAsync("() => globalThis.weakRef.deref()")); + + await Page.EvaluateAsync("() => globalThis.objectToDestroy = null"); + await Page.RequestGCAsync(); + Assert.IsNull(await Page.EvaluateAsync("() => globalThis.weakRef.deref()")); + } +} diff --git a/src/Playwright.Tests/PageRouteWebSocketTests.cs b/src/Playwright.Tests/PageRouteWebSocketTests.cs new file mode 100644 index 0000000000..79eb4b116c --- /dev/null +++ b/src/Playwright.Tests/PageRouteWebSocketTests.cs @@ -0,0 +1,287 @@ +/* + * MIT License + * + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System.Text.RegularExpressions; + +namespace Microsoft.Playwright.Tests; + +public class PageRouteWebSocketTests : PageTestEx +{ + private Task SetupWS(IPage page, int port, string path) => SetupWS(page.MainFrame, port, path); + + private async Task SetupWS(IFrame target, int port, string path) + { + await target.GotoAsync("about:blank"); + await target.EvaluateAsync(@"({ port, binaryType }) => { + window.log = []; + window.ws = new WebSocket('ws://localhost:' + port + '/ws'); + window.ws.binaryType = binaryType; + window.ws.addEventListener('open', () => window.log.push('open')); + window.ws.addEventListener('close', event => window.log.push(`close code=${event.code} reason=${event.reason} wasClean=${event.wasClean}`)); + window.ws.addEventListener('error', event => window.log.push(`error`)); + window.ws.addEventListener('message', async event => { + let data; + if (typeof event.data === 'string') + data = event.data; + else if (event.data instanceof Blob) + data = 'blob:' + await event.data.text(); + else + data = 'arraybuffer:' + await (new Blob([event.data])).text(); + window.log.push(`message: data=${data} origin=${event.origin} lastEventId=${event.lastEventId}`); + }); + window.wsOpened = new Promise(f => window.ws.addEventListener('open', () => f())); + }", new Dictionary + { + ["port"] = port, + ["binaryType"] = path + }); + } + + async Task AssertAreEqualWithRetriesAsync(Func> func, string[] expected) + { + var maxTimeout = 5_000; + var start = DateTime.Now; + while (true) + { + var result = await func(); + if (result.SequenceEqual(expected)) + { + return; + } + Console.WriteLine($"Actual : {string.Join(", ", result)}"); + Console.WriteLine($"Expected: {string.Join(", ", expected)}"); + if (DateTime.Now - start > TimeSpan.FromMilliseconds(maxTimeout)) + { + Assert.AreEqual(expected, result); + } + await Task.Delay(100); + } + } + + [PlaywrightTest("page-route-web-socket.spec.ts", "should work with ws.close")] + public async Task ShouldworkWithWSClose() + { + var tcs = new TaskCompletionSource(); + await Page.RouteWebSocketAsync(new Regex($".*"), ws => + { + ws.ConnectToServer(); + tcs.SetResult(ws); + }); + + var wsTask = Server.WaitForWebSocketAsync(); + await SetupWS(Page, Server.Port, "blob"); + var ws = await wsTask; + + var route = await tcs.Task; + route.Send("hello"); + await AssertAreEqualWithRetriesAsync(() => Page.EvaluateAsync(@"() => window.log"), new[] { "open", $"message: data=hello origin=ws://localhost:{Server.Port} lastEventId=" }); + } + + [PlaywrightTest("page-route-web-socket.spec.ts", "should pattern match")] + public async Task ShouldPatternMatch() + { + await Page.RouteWebSocketAsync(new Regex(".*\\/ws$"), ws => + { + ws.ConnectToServer(); + }); + + await Page.RouteWebSocketAsync("**/mock-ws", ws => + { + ws.OnMessage(message => + { + ws.Send("mock-response"); + }); + }); + + var wsPromise = Server.WaitForWebSocketAsync(); + + await Page.GotoAsync("about:blank"); + await Page.EvaluateAsync(@"async ({ port }) => { + window.log = []; + window.ws1 = new WebSocket('ws://localhost:' + port + '/ws'); + window.ws1.addEventListener('message', event => window.log.push(`ws1:${event.data}`)); + window.ws2 = new WebSocket('ws://localhost:' + port + '/something/something/mock-ws'); + window.ws2.addEventListener('message', event => window.log.push(`ws2:${event.data}`)); + await Promise.all([ + new Promise(f => window.ws1.addEventListener('open', f)), + new Promise(f => window.ws2.addEventListener('open', f)), + ]); + }", new Dictionary + { + ["port"] = Server.Port + }); + + using var ws = await wsPromise; + ws.MessageReceived += async (message, _) => + { + await ws.SendAsync("response").ConfigureAwait(false); + }; + + await Page.EvaluateAsync("() => window.ws1.send('request')"); + await AssertAreEqualWithRetriesAsync(() => Page.EvaluateAsync("() => window.log"), new[] { "ws1:response" }); + + await Page.EvaluateAsync("() => window.ws2.send('request')"); + await AssertAreEqualWithRetriesAsync(() => Page.EvaluateAsync("() => window.log"), new[] { "ws1:response", "ws2:mock-response" }); + } + + [PlaywrightTest("page-route-web-socket.spec.ts", "should work with server")] + public async Task ShouldWorkWithServer() + { + var tcs = new TaskCompletionSource(); + await Page.RouteWebSocketAsync(new Regex(".*"), ws => + { + var server = ws.ConnectToServer(); + ws.OnMessage(message => + { + switch (message.Text) + { + case "to-respond": + ws.Send("response"); + return; + case "to-block": + return; + case "to-modify": + server.Send("modified"); + return; + } + server.Send(message.Text); + }); + server.OnMessage(message => + { + switch (message.Text) + { + case "to-block": + return; + case "to-modify": + ws.Send("modified"); + return; + } + ws.Send(message.Text); + }); + server.Send("fake"); + tcs.SetResult(ws); + }); + + var wsTask = Server.WaitForWebSocketAsync(); + var log = new List(); + Server.OnceWebSocketConnection((ws, _) => + { + ws.MessageReceived += (_, message) => log.Add($"message: {message}"); + ws.Closed += (_, ev) => log.Add($"close: code={ev.Code} reason={ev.Reason}"); + return Task.CompletedTask; + }); + + await SetupWS(Page, Server.Port, "blob"); + var ws = await wsTask; + await AssertAreEqualWithRetriesAsync(() => Task.FromResult(log.ToArray()), new[] { "message: fake" }); + + await ws.SendAsync("to-modify"); + await ws.SendAsync("to-block"); + await ws.SendAsync("pass-server"); + await AssertAreEqualWithRetriesAsync(() => Page.EvaluateAsync("() => window.log"), new[] + { + "open", + $"message: data=modified origin=ws://localhost:{Server.Port} lastEventId=", + $"message: data=pass-server origin=ws://localhost:{Server.Port} lastEventId=", + }); + + await Page.EvaluateAsync(@"() => { + window.ws.send('to-respond'); + window.ws.send('to-modify'); + window.ws.send('to-block'); + window.ws.send('pass-client'); + }"); + await AssertAreEqualWithRetriesAsync(() => Task.FromResult(log.ToArray()), new[] { "message: fake", "message: modified", "message: pass-client" }); + await AssertAreEqualWithRetriesAsync(() => Page.EvaluateAsync("() => window.log"), new[] + { + "open", + $"message: data=modified origin=ws://localhost:{Server.Port} lastEventId=", + $"message: data=pass-server origin=ws://localhost:{Server.Port} lastEventId=", + $"message: data=response origin=ws://localhost:{Server.Port} lastEventId=", + }); + + var route = await tcs.Task; + route.Send("another"); + await AssertAreEqualWithRetriesAsync(() => Page.EvaluateAsync("() => window.log"), new[] + { + "open", + $"message: data=modified origin=ws://localhost:{Server.Port} lastEventId=", + $"message: data=pass-server origin=ws://localhost:{Server.Port} lastEventId=", + $"message: data=response origin=ws://localhost:{Server.Port} lastEventId=", + $"message: data=another origin=ws://localhost:{Server.Port} lastEventId=", + }); + + await Page.EvaluateAsync("() => { window.ws.send('pass-client-2'); }"); + await AssertAreEqualWithRetriesAsync(() => Task.FromResult(log.ToArray()), new[] { "message: fake", "message: modified", "message: pass-client", "message: pass-client-2" }); + + await Page.EvaluateAsync("() => { window.ws.close(3009, 'problem'); }"); + await AssertAreEqualWithRetriesAsync(() => Task.FromResult(log.ToArray()), new[] { "message: fake", "message: modified", "message: pass-client", "message: pass-client-2", "close: code=3009 reason=problem" }); + } + + [PlaywrightTest("page-route-web-socket.spec.ts", "should work without server")] + public async Task ShouldWorkWithoutServer() + { + var tcs = new TaskCompletionSource(); + await Page.RouteWebSocketAsync(new Regex(".*"), ws => + { + ws.OnMessage(message => + { + if (message.Text == "to-respond") + { + ws.Send("response"); + } + }); + tcs.SetResult(ws); + }); + + await SetupWS(Page, Server.Port, "blob"); + + await Page.EvaluateAsync(@"async () => { + await window.wsOpened; + window.ws.send('to-respond'); + window.ws.send('to-block'); + window.ws.send('to-respond'); + }"); + + await AssertAreEqualWithRetriesAsync(() => Page.EvaluateAsync("() => window.log"), new[] + { + "open", + $"message: data=response origin=ws://localhost:{Server.Port} lastEventId=", + $"message: data=response origin=ws://localhost:{Server.Port} lastEventId=", + }); + + var route = await tcs.Task; + route.Send("another"); + await route.CloseAsync(new() { Code = 3008, Reason = "oops" }); + + await AssertAreEqualWithRetriesAsync(() => Page.EvaluateAsync("() => window.log"), + [ + "open", + $"message: data=response origin=ws://localhost:{Server.Port} lastEventId=", + $"message: data=response origin=ws://localhost:{Server.Port} lastEventId=", + $"message: data=another origin=ws://localhost:{Server.Port} lastEventId=", + "close code=3008 reason=oops wasClean=true", + ]); + } +} \ No newline at end of file diff --git a/src/Playwright/API/Generated/IAPIRequestContext.cs b/src/Playwright/API/Generated/IAPIRequestContext.cs index f90ff89964..29e47d1798 100644 --- a/src/Playwright/API/Generated/IAPIRequestContext.cs +++ b/src/Playwright/API/Generated/IAPIRequestContext.cs @@ -95,8 +95,7 @@ public partial interface IAPIRequestContext /// /// /// The common way to send file(s) in the body of a request is to upload them as form - /// fields with multipart/form-data encoding. Use to - /// construct request body and pass it to the request as + /// fields with multipart/form-data encoding, by specifiying the multipart /// parameter: /// /// @@ -132,8 +131,7 @@ public partial interface IAPIRequestContext /// /// /// The common way to send file(s) in the body of a request is to upload them as form - /// fields with multipart/form-data encoding. Use to - /// construct request body and pass it to the request as + /// fields with multipart/form-data encoding, by specifiying the multipart /// parameter: /// /// diff --git a/src/Playwright/API/Generated/IAccessibility.cs b/src/Playwright/API/Generated/IAccessibility.cs index 8e1c0c1d2d..c99b530376 100644 --- a/src/Playwright/API/Generated/IAccessibility.cs +++ b/src/Playwright/API/Generated/IAccessibility.cs @@ -69,6 +69,11 @@ public partial interface IAccessibility /// Captures the current state of the accessibility tree. The returned object represents /// the root accessible node of the page. /// + /// + /// The Chromium accessibility tree contains nodes that go unused on most platforms + /// and by most screen readers. Playwright will discard them as well for an easier to + /// process tree, unless is set to false. + /// /// **Usage** /// An example of dumping the entire accessibility tree: /// @@ -85,7 +90,8 @@ public partial interface IAccessibility /// /// The Chromium accessibility tree contains nodes that go unused on most platforms /// and by most screen readers. Playwright will discard them as well for an easier to - /// process tree, unless is set to false. + /// process tree, unless is set to false. + /// /// /// /// Call options diff --git a/src/Playwright/API/Generated/IBrowser.cs b/src/Playwright/API/Generated/IBrowser.cs index a283462dfd..80ccfe95d3 100644 --- a/src/Playwright/API/Generated/IBrowser.cs +++ b/src/Playwright/API/Generated/IBrowser.cs @@ -73,6 +73,11 @@ public partial interface IBrowser /// browser and disconnects from the browser server. /// /// + /// This is similar to force quitting the browser. Therefore, you should call + /// on any 's you explicitly created earlier with **before** calling . + /// + /// /// The object itself is considered to be disposed and cannot /// be used anymore. /// @@ -82,6 +87,7 @@ public partial interface IBrowser /// This is similar to force quitting the browser. Therefore, you should call /// on any 's you explicitly created earlier with **before** calling . + /// /// /// /// Call options @@ -106,12 +112,22 @@ public partial interface IBrowser /// Indicates that the browser is connected. bool IsConnected { get; } - /// Returns the newly created browser session. + /// + /// CDP Sessions are only supported on Chromium-based browsers. + /// Returns the newly created browser session. + /// /// CDP Sessions are only supported on Chromium-based browsers. Task NewBrowserCDPSessionAsync(); /// /// Creates a new browser context. It won't share cookies/cache with other browser contexts. + /// + /// If directly using this method to create s, it is best + /// practice to explicitly close the returned context via + /// when your code is done with the , and before calling + /// . This will ensure the context is closed + /// gracefully and any artifacts—like HARs and videos—are fully flushed and saved. + /// /// **Usage** /// /// using var playwright = await Playwright.CreateAsync();
diff --git a/src/Playwright/API/Generated/IBrowserContext.cs b/src/Playwright/API/Generated/IBrowserContext.cs index d0e3146a1a..57755cf6e5 100644 --- a/src/Playwright/API/Generated/IBrowserContext.cs +++ b/src/Playwright/API/Generated/IBrowserContext.cs @@ -56,6 +56,7 @@ namespace Microsoft.Playwright; public partial interface IBrowserContext { /// + /// Only works with Chromium browser's persistent context. /// Emitted when new background page is created in the context. /// /// context.BackgroundPage += (_, backgroundPage) =>
@@ -120,6 +121,10 @@ public partial interface IBrowserContext /// await dialog.AcceptAsync();
/// }; ///
+ /// + /// When no or listeners + /// are present, all dialogs are automatically dismissed. + /// ///
/// /// @@ -150,6 +155,10 @@ public partial interface IBrowserContext /// });
/// Console.WriteLine(await popup.EvaluateAsync<string>("location.href")); ///
+ /// + /// Use to wait until the page gets to a particular + /// state (you should not need it in most cases). + /// ///
/// /// @@ -185,6 +194,11 @@ public partial interface IBrowserContext /// Emitted when a request fails, for example by timing out. To only listen for failed /// requests from a particular page, use . /// + /// + /// HTTP Error responses, such as 404 or 503, are still successful responses from HTTP + /// standpoint, so request will complete with + /// event and not with . + /// /// /// /// @@ -243,6 +257,10 @@ public partial interface IBrowserContext /// **Usage** /// An example of overriding Math.random before the page loads: /// await Context.AddInitScriptAsync(scriptPath: "preload.js"); + /// + /// The order of evaluation of multiple scripts installed via + /// and is not defined. + /// /// /// /// @@ -254,7 +272,10 @@ public partial interface IBrowserContext /// Instead of specifying , gives the file name to load from. Task AddInitScriptAsync(string? script = default, string? scriptPath = default); - /// All existing background pages in the context. + /// + /// Background pages are only supported on Chromium-based browsers. + /// All existing background pages in the context. + /// /// Background pages are only supported on Chromium-based browsers. IReadOnlyList BackgroundPages { get; } @@ -299,6 +320,7 @@ public partial interface IBrowserContext /// Closes the browser context. All the pages that belong to the browser context will /// be closed. /// + /// The default browser context cannot be closed. /// /// The default browser context cannot be closed. /// Call options @@ -315,16 +337,17 @@ public partial interface IBrowserContext /// /// - /// The method adds a function called on the window object - /// of every frame in every page in the context. When called, the function executes - /// and returns a which resolves to the - /// return value of . If the - /// returns a , it will be awaited. + /// The method adds a function called + /// on the window object of every frame in every page in the context. When called, + /// the function executes and returns + /// a which resolves to the return value of . + /// If the returns a , + /// it will be awaited. /// /// - /// The first argument of the function contains information - /// about the caller: { browserContext: BrowserContext, page: Page, frame: Frame - /// }. + /// The first argument of the function + /// contains information about the caller: { browserContext: BrowserContext, page: + /// Page, frame: Frame }. /// /// See for page-only version. /// **Usage** @@ -355,12 +378,15 @@ public partial interface IBrowserContext /// /// - /// The method adds a function called on the window object - /// of every frame in every page in the context. When called, the function executes - /// and returns a which resolves to the - /// return value of . + /// The method adds a function called + /// on the window object of every frame in every page in the context. When called, + /// the function executes and returns + /// a which resolves to the return value of . + /// + /// + /// If the returns a , + /// it will be awaited. /// - /// If the returns a , it will be awaited. /// See for page-only version. /// **Usage** /// An example of adding a sha256 function to all pages in the context: @@ -434,7 +460,10 @@ public partial interface IBrowserContext /// Call options Task GrantPermissionsAsync(IEnumerable permissions, BrowserContextGrantPermissionsOptions? options = default); - /// Returns the newly created session. + /// + /// CDP sessions are only supported on Chromium-based browsers. + /// Returns the newly created session. + /// /// CDP sessions are only supported on Chromium-based browsers. /// /// Target to create new session for. For backwards-compatibility, this parameter is @@ -442,7 +471,10 @@ public partial interface IBrowserContext /// Task NewCDPSessionAsync(IPage page); - /// Returns the newly created session. + /// + /// CDP sessions are only supported on Chromium-based browsers. + /// Returns the newly created session. + /// /// CDP sessions are only supported on Chromium-based browsers. /// /// Target to create new session for. For backwards-compatibility, this parameter is @@ -470,6 +502,12 @@ public partial interface IBrowserContext /// page in the browser context. Once route is enabled, every request matching the url /// pattern will stall unless it's continued, fulfilled or aborted. /// + /// + /// will not intercept requests intercepted + /// by Service Worker. See this + /// issue. We recommend disabling Service Workers when using request interception by + /// setting to 'block'. + /// /// **Usage** /// An example of a naive handler that aborts all image requests: /// @@ -505,20 +543,21 @@ public partial interface IBrowserContext /// context routes when request matches both handlers. /// /// To remove a route with its handler you can use . + /// Enabling routing disables http cache. /// /// /// /// will not intercept requests intercepted /// by Service Worker. See this /// issue. We recommend disabling Service Workers when using request interception by - /// setting to 'block'. + /// setting to 'block'. /// /// Enabling routing disables http cache. /// /// /// A glob pattern, regex pattern or predicate receiving to match - /// while routing. When a via the context options was provided - /// and the passed URL is a path, it gets merged via the new + /// while routing. When a via the context options + /// was provided and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// handler function to route the request. @@ -531,6 +570,12 @@ public partial interface IBrowserContext /// page in the browser context. Once route is enabled, every request matching the url /// pattern will stall unless it's continued, fulfilled or aborted. /// + /// + /// will not intercept requests intercepted + /// by Service Worker. See this + /// issue. We recommend disabling Service Workers when using request interception by + /// setting to 'block'. + /// /// **Usage** /// An example of a naive handler that aborts all image requests: /// @@ -566,20 +611,21 @@ public partial interface IBrowserContext /// context routes when request matches both handlers. /// /// To remove a route with its handler you can use . + /// Enabling routing disables http cache. /// /// /// /// will not intercept requests intercepted /// by Service Worker. See this /// issue. We recommend disabling Service Workers when using request interception by - /// setting to 'block'. + /// setting to 'block'. /// /// Enabling routing disables http cache. /// /// /// A glob pattern, regex pattern or predicate receiving to match - /// while routing. When a via the context options was provided - /// and the passed URL is a path, it gets merged via the new + /// while routing. When a via the context options + /// was provided and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// handler function to route the request. @@ -592,6 +638,12 @@ public partial interface IBrowserContext /// page in the browser context. Once route is enabled, every request matching the url /// pattern will stall unless it's continued, fulfilled or aborted. /// + /// + /// will not intercept requests intercepted + /// by Service Worker. See this + /// issue. We recommend disabling Service Workers when using request interception by + /// setting to 'block'. + /// /// **Usage** /// An example of a naive handler that aborts all image requests: /// @@ -627,20 +679,21 @@ public partial interface IBrowserContext /// context routes when request matches both handlers. /// /// To remove a route with its handler you can use . + /// Enabling routing disables http cache. /// /// /// /// will not intercept requests intercepted /// by Service Worker. See this /// issue. We recommend disabling Service Workers when using request interception by - /// setting to 'block'. + /// setting to 'block'. /// /// Enabling routing disables http cache. /// /// /// A glob pattern, regex pattern or predicate receiving to match - /// while routing. When a via the context options was provided - /// and the passed URL is a path, it gets merged via the new + /// while routing. When a via the context options + /// was provided and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// handler function to route the request. @@ -657,7 +710,7 @@ public partial interface IBrowserContext /// Playwright will not serve requests intercepted by Service Worker from the HAR file. /// See this issue. /// We recommend disabling Service Workers when using request interception by setting - /// to 'block'. + /// to 'block'. /// /// /// @@ -668,6 +721,102 @@ public partial interface IBrowserContext /// Call options Task RouteFromHARAsync(string har, BrowserContextRouteFromHAROptions? options = default); + /// + /// + /// This method allows to modify websocket connections that are made by any page in + /// the browser context. + /// + /// + /// Note that only WebSockets created after this method was called will be routed. + /// It is recommended to call this method before creating any pages. + /// + /// **Usage** + /// + /// Below is an example of a simple handler that blocks some websocket messages. See + /// for more details and examples. + /// + /// + /// await context.RouteWebSocketAsync("/ws", async ws => {
+ /// ws.RouteSend(message => {
+ /// if (message == "to-be-blocked")
+ /// return;
+ /// ws.Send(message);
+ /// });
+ /// await ws.ConnectAsync();
+ /// }); + ///
+ ///
+ /// + /// Only WebSockets with the url matching this pattern will be routed. A string pattern + /// can be relative to the context option. + /// + /// Handler function to route the WebSocket. + Task RouteWebSocketAsync(string url, Action handler); + + /// + /// + /// This method allows to modify websocket connections that are made by any page in + /// the browser context. + /// + /// + /// Note that only WebSockets created after this method was called will be routed. + /// It is recommended to call this method before creating any pages. + /// + /// **Usage** + /// + /// Below is an example of a simple handler that blocks some websocket messages. See + /// for more details and examples. + /// + /// + /// await context.RouteWebSocketAsync("/ws", async ws => {
+ /// ws.RouteSend(message => {
+ /// if (message == "to-be-blocked")
+ /// return;
+ /// ws.Send(message);
+ /// });
+ /// await ws.ConnectAsync();
+ /// }); + ///
+ ///
+ /// + /// Only WebSockets with the url matching this pattern will be routed. A string pattern + /// can be relative to the context option. + /// + /// Handler function to route the WebSocket. + Task RouteWebSocketAsync(Regex url, Action handler); + + /// + /// + /// This method allows to modify websocket connections that are made by any page in + /// the browser context. + /// + /// + /// Note that only WebSockets created after this method was called will be routed. + /// It is recommended to call this method before creating any pages. + /// + /// **Usage** + /// + /// Below is an example of a simple handler that blocks some websocket messages. See + /// for more details and examples. + /// + /// + /// await context.RouteWebSocketAsync("/ws", async ws => {
+ /// ws.RouteSend(message => {
+ /// if (message == "to-be-blocked")
+ /// return;
+ /// ws.Send(message);
+ /// });
+ /// await ws.ConnectAsync();
+ /// }); + ///
+ ///
+ /// + /// Only WebSockets with the url matching this pattern will be routed. A string pattern + /// can be relative to the context option. + /// + /// Handler function to route the WebSocket. + Task RouteWebSocketAsync(Func url, Action handler); + /// /// /// This setting will change the default maximum navigation time for the following methods @@ -681,6 +830,10 @@ public partial interface IBrowserContext /// /// /// + /// + /// and + /// take priority over . + /// /// /// /// @@ -694,7 +847,12 @@ public partial interface IBrowserContext /// /// /// This setting will change the default maximum time for all the methods accepting - /// option. + /// option. + /// + /// + /// , + /// and take priority over + /// . /// /// /// @@ -715,6 +873,10 @@ public partial interface IBrowserContext /// header, page-specific header value will be used instead of the browser context header /// value. /// + /// + /// does not guarantee the order + /// of headers in the outgoing requests. + /// /// /// /// @@ -741,6 +903,10 @@ public partial interface IBrowserContext /// Longitude = 30.31667f
/// }); ///
+ /// + /// Consider using to grant permissions + /// for the browser context pages to read its geolocation. + /// /// /// /// @@ -777,8 +943,9 @@ public partial interface IBrowserContext /// /// - /// Removes a route created with . When is not specified, removes all routes for the . + /// Removes a route created with . When is not specified, removes all routes for the + /// . /// /// /// @@ -790,8 +957,9 @@ public partial interface IBrowserContext /// /// - /// Removes a route created with . When is not specified, removes all routes for the . + /// Removes a route created with . When is not specified, removes all routes for the + /// . /// /// /// @@ -803,8 +971,9 @@ public partial interface IBrowserContext /// /// - /// Removes a route created with . When is not specified, removes all routes for the . + /// Removes a route created with . When is not specified, removes all routes for the + /// . /// /// /// diff --git a/src/Playwright/API/Generated/IBrowserType.cs b/src/Playwright/API/Generated/IBrowserType.cs index 8cd32386aa..be07c7f4a7 100644 --- a/src/Playwright/API/Generated/IBrowserType.cs +++ b/src/Playwright/API/Generated/IBrowserType.cs @@ -73,6 +73,10 @@ public partial interface IBrowserType /// DevTools Protocol. /// /// The default browser context is accessible via . + /// + /// Connecting over the Chrome DevTools Protocol is only supported for Chromium-based + /// browsers. + /// /// **Usage** /// /// var browser = await playwright.Chromium.ConnectOverCDPAsync("http://localhost:9222");
@@ -100,7 +104,7 @@ public partial interface IBrowserType /// Returns the browser instance. /// **Usage** /// - /// You can use to filter out --mute-audio + /// You can use to filter out --mute-audio /// from default arguments: /// /// @@ -111,7 +115,7 @@ public partial interface IBrowserType /// /// > **Chromium-only** Playwright can also be used to control the Google Chrome or /// Microsoft Edge browsers, but it works best with the version of Chromium it is bundled - /// with. There is no guarantee it will work with any other version. Use + /// with. There is no guarantee it will work with any other version. Use /// option with extreme caution. /// /// > @@ -134,7 +138,7 @@ public partial interface IBrowserType /// /// Returns the persistent browser context instance. /// - /// Launches browser that uses persistent storage located at + /// Launches browser that uses persistent storage located at /// and returns the only context. Closing this context will automatically close the /// browser. /// diff --git a/src/Playwright/API/Generated/IDialog.cs b/src/Playwright/API/Generated/IDialog.cs index 12d61971a2..781a63b459 100644 --- a/src/Playwright/API/Generated/IDialog.cs +++ b/src/Playwright/API/Generated/IDialog.cs @@ -56,6 +56,12 @@ namespace Microsoft.Playwright; /// }
/// } ///
+/// +/// Dialogs are dismissed automatically, unless there is a +/// listener. When listener is present, it **must** either +/// or the dialog - otherwise the page will freeze +/// waiting for the dialog, and actions like click will never finish. +/// ///
/// /// diff --git a/src/Playwright/API/Generated/IElementHandle.cs b/src/Playwright/API/Generated/IElementHandle.cs index 5ebc5733f8..dcff4e85a8 100644 --- a/src/Playwright/API/Generated/IElementHandle.cs +++ b/src/Playwright/API/Generated/IElementHandle.cs @@ -35,6 +35,10 @@ namespace Microsoft.Playwright; /// ElementHandle represents an in-page DOM element. ElementHandles can be created with /// the method. /// +/// +/// The use of ElementHandle is discouraged, use objects and +/// web-first assertions instead. +/// /// /// var handle = await page.QuerySelectorAsync("a");
/// await handle.ClickAsync(); @@ -122,7 +126,8 @@ public partial interface IElementHandle : IJSHandle /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is + /// set. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. @@ -133,7 +138,7 @@ public partial interface IElementHandle : IJSHandle /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -150,15 +155,16 @@ public partial interface IElementHandle : IJSHandle /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is + /// set. /// /// Scroll the element into view if needed. /// /// Use to click in the center of the element, or the specified - /// . + /// . /// /// - /// Wait for initiated navigations to either succeed or fail, unless + /// Wait for initiated navigations to either succeed or fail, unless /// option is set. /// /// @@ -167,7 +173,7 @@ public partial interface IElementHandle : IJSHandle /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -192,12 +198,13 @@ public partial interface IElementHandle : IJSHandle /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option + /// is set. /// /// Scroll the element into view if needed. /// /// Use to double click in the center of the element, or the - /// specified . + /// specified . /// /// /// @@ -205,10 +212,14 @@ public partial interface IElementHandle : IJSHandle /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// + /// + /// elementHandle.dblclick() dispatches two click events and a single + /// dblclick event. + /// /// /// /// @@ -232,13 +243,14 @@ public partial interface IElementHandle : IJSHandle /// **Usage** /// await elementHandle.DispatchEventAsync("click"); /// - /// Under the hood, it creates an instance of an event based on the given , initializes it with properties and dispatches - /// it on the element. Events are composed, cancelable and bubble by default. + /// Under the hood, it creates an instance of an event based on the given , + /// initializes it with properties and + /// dispatches it on the element. Events are composed, cancelable and + /// bubble by default. /// /// - /// Since is event-specific, please refer to the events - /// documentation for the lists of initial properties: + /// Since is event-specific, please + /// refer to the events documentation for the lists of initial properties: /// /// /// DeviceMotionEvent @@ -274,15 +286,16 @@ public partial interface IElementHandle : IJSHandle /// can lead to the flaky tests. Use , other helper methods or web-first assertions instead. /// - /// Returns the return value of . + /// Returns the return value of . /// /// The method finds an element matching the specified selector in the ElementHandles - /// subtree and passes it as a first argument to . If no - /// elements match the selector, the method throws an error. + /// subtree and passes it as a first argument to . + /// If no elements match the selector, the method throws an error. /// /// - /// If returns a , then - /// would wait for the promise to resolve and return its value. + /// If returns a , + /// then would wait for the promise + /// to resolve and return its value. /// /// **Usage** /// @@ -296,7 +309,7 @@ public partial interface IElementHandle : IJSHandle /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvalOnSelectorAsync(string selector, string expression, object? arg = default); /// @@ -304,15 +317,15 @@ public partial interface IElementHandle : IJSHandle /// In most cases, , other /// helper methods and web-first assertions do a better job. /// - /// Returns the return value of . + /// Returns the return value of . /// /// The method finds all elements matching the specified selector in the ElementHandle's - /// subtree and passes an array of matched elements as a first argument to . + /// subtree and passes an array of matched elements as a first argument to . /// /// - /// If returns a , then - /// would wait for the promise to resolve and return its value. + /// If returns a , + /// then would wait for the promise + /// to resolve and return its value. /// /// **Usage** /// @@ -325,7 +338,7 @@ public partial interface IElementHandle : IJSHandle /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvalOnSelectorAllAsync(string selector, string expression, object? arg = default); /// @@ -381,12 +394,13 @@ public partial interface IElementHandle : IJSHandle /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is + /// set. /// /// Scroll the element into view if needed. /// /// Use to hover over the center of the element, or the specified - /// . + /// . /// /// /// @@ -394,7 +408,7 @@ public partial interface IElementHandle : IJSHandle /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -505,9 +519,9 @@ public partial interface IElementHandle : IJSHandle /// /// Focuses the element, and then uses and . /// - /// can specify the intended keyboardEvent.key - /// value or a single character to generate the text for. A superset of the values can be found here. + /// can specify the intended keyboardEvent.key + /// value or a single character to generate the text for. A superset of the + /// values can be found here. /// Examples of the keys are: /// /// @@ -522,12 +536,12 @@ public partial interface IElementHandle : IJSHandle /// Alt, Meta, ShiftLeft, ControlOrMeta. /// /// - /// Holding down Shift will type the text that corresponds to the + /// Holding down Shift will type the text that corresponds to the /// in the upper case. /// /// - /// If is a single character, it is case-sensitive, so the values - /// a and A will generate different respective texts. + /// If is a single character, it is case-sensitive, + /// so the values a and A will generate different respective texts. /// /// /// Shortcuts such as key: "Control+o", key: "Control++ or key: "Control+Shift+T" @@ -905,15 +919,16 @@ public partial interface IElementHandle : IJSHandle /// If the element already has the right checked state, this method returns immediately. /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless + /// option is set. If the element is detached during the checks, the whole action is + /// retried. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. /// Ensure that the element is now checked or unchecked. If not, this method throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -1020,12 +1035,12 @@ public partial interface IElementHandle : IJSHandle /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is set. /// /// Scroll the element into view if needed. /// /// Use to tap the center of the element, or the specified - /// . + /// . /// /// /// @@ -1033,10 +1048,14 @@ public partial interface IElementHandle : IJSHandle /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// + /// + /// elementHandle.tap() requires that the hasTouch option of the browser + /// context be set to true. + /// /// /// /// @@ -1087,7 +1106,8 @@ public partial interface IElementHandle : IJSHandle /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is + /// set. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. @@ -1098,7 +1118,7 @@ public partial interface IElementHandle : IJSHandle /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -1107,10 +1127,10 @@ public partial interface IElementHandle : IJSHandle Task UncheckAsync(ElementHandleUncheckOptions? options = default); /// - /// Returns when the element satisfies the . + /// Returns when the element satisfies the . /// - /// Depending on the parameter, this method waits for one of - /// the actionability + /// Depending on the parameter, + /// this method waits for one of the actionability /// checks to pass. This method throws when the element is detached while waiting, unless /// waiting for the "hidden" state. /// @@ -1133,7 +1153,7 @@ public partial interface IElementHandle : IJSHandle /// "editable" Wait until the element is editable. /// /// - /// If the element does not satisfy the condition for the + /// If the element does not satisfy the condition for the /// milliseconds, this method will throw. /// /// @@ -1147,16 +1167,17 @@ public partial interface IElementHandle : IJSHandle /// instead. /// /// - /// Returns element specified by selector when it satisfies + /// Returns element specified by selector when it satisfies /// option. Returns null if waiting for hidden or detached. /// /// - /// Wait for the relative to the element handle to satisfy - /// option (either appear/disappear from dom, or become visible/hidden). - /// If at the moment of calling the method already satisfies - /// the condition, the method will return immediately. If the selector doesn't satisfy - /// the condition for the milliseconds, the function will - /// throw. + /// Wait for the relative to the element + /// handle to satisfy option (either + /// appear/disappear from dom, or become visible/hidden). If at the moment of calling + /// the method already satisfies the + /// condition, the method will return immediately. If the selector doesn't satisfy the + /// condition for the milliseconds, + /// the function will throw. /// /// **Usage** /// @@ -1165,6 +1186,10 @@ public partial interface IElementHandle : IJSHandle /// // Waiting for the "span" selector relative to the div.
/// var span = await page.WaitForSelectorAsync("span", WaitForSelectorState.Attached); ///
+ /// + /// This method does not work across navigations, use + /// instead. + /// ///
/// /// diff --git a/src/Playwright/API/Generated/IFrame.cs b/src/Playwright/API/Generated/IFrame.cs index 0748492c42..af7a922c88 100644 --- a/src/Playwright/API/Generated/IFrame.cs +++ b/src/Playwright/API/Generated/IFrame.cs @@ -112,13 +112,13 @@ public partial interface IFrame /// href="https://playwright.dev/dotnet/docs/locators">locators. /// /// - /// This method checks an element matching by performing + /// This method checks an element matching by performing /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Ensure that matched element is a checkbox or a radio input. If not, this method @@ -126,15 +126,15 @@ public partial interface IFrame /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is + /// set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. /// Ensure that the element is now checked. If not, this method throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -154,31 +154,31 @@ public partial interface IFrame /// href="https://playwright.dev/dotnet/docs/locators">locators. /// /// - /// This method clicks an element matching by performing + /// This method clicks an element matching by performing /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is + /// set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// /// Use to click in the center of the element, or the specified - /// . + /// . /// /// - /// Wait for initiated navigations to either succeed or fail, unless + /// Wait for initiated navigations to either succeed or fail, unless /// option is set. /// /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -199,31 +199,35 @@ public partial interface IFrame /// locators. /// /// - /// This method double clicks an element matching by performing - /// the following steps: + /// This method double clicks an element matching + /// by performing the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option + /// is set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// /// Use to double click in the center of the element, or the - /// specified . if the first click of the dblclick() + /// specified . if the first click of the dblclick() /// triggers a navigation event, this method will throw. /// /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// + /// + /// frame.dblclick() dispatches two click events and a single dblclick + /// event. + /// /// /// /// @@ -251,13 +255,13 @@ public partial interface IFrame /// **Usage** /// await frame.DispatchEventAsync("button#submit", "click"); /// - /// Under the hood, it creates an instance of an event based on the given , initializes it with properties and dispatches + /// Under the hood, it creates an instance of an event based on the given , + /// initializes it with properties and dispatches /// it on the element. Events are composed, cancelable and bubble by default. /// /// - /// Since is event-specific, please refer to the events - /// documentation for the lists of initial properties: + /// Since is event-specific, please refer to + /// the events documentation for the lists of initial properties: /// /// /// DeviceMotionEvent @@ -307,15 +311,16 @@ public partial interface IFrame /// can lead to the flaky tests. Use , other helper methods or web-first assertions instead. /// - /// Returns the return value of . + /// Returns the return value of . /// /// The method finds an element matching the specified selector within the frame and - /// passes it as a first argument to . If no elements match - /// the selector, the method throws an error. + /// passes it as a first argument to . If no + /// elements match the selector, the method throws an error. /// /// - /// If returns a , then - /// would wait for the promise to resolve and return its value. + /// If returns a , then would wait for the promise to resolve and return + /// its value. /// /// **Usage** /// @@ -329,7 +334,7 @@ public partial interface IFrame /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . /// Call options Task EvalOnSelectorAsync(string selector, string expression, object? arg = default, FrameEvalOnSelectorOptions? options = default); @@ -338,14 +343,15 @@ public partial interface IFrame /// In most cases, , other /// helper methods and web-first assertions do a better job. /// - /// Returns the return value of . + /// Returns the return value of . /// /// The method finds all elements matching the specified selector within the frame and - /// passes an array of matched elements as a first argument to . + /// passes an array of matched elements as a first argument to . /// /// - /// If returns a , then - /// would wait for the promise to resolve and return its value. + /// If returns a , then + /// would wait for the promise to resolve + /// and return its value. /// /// **Usage** /// var divsCount = await frame.EvalOnSelectorAllAsync<bool>("div", "(divs, min) => divs.length >= min", 10); @@ -355,11 +361,11 @@ public partial interface IFrame /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvalOnSelectorAllAsync(string selector, string expression, object? arg = default); /// - /// Returns the return value of . + /// Returns the return value of . /// /// If the function passed to the returns a , then would wait for the promise @@ -392,11 +398,11 @@ public partial interface IFrame /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvaluateAsync(string expression, object? arg = default); /// - /// Returns the return value of as a . + /// Returns the return value of as a . /// /// The only difference between and /// is that returns . @@ -425,15 +431,15 @@ public partial interface IFrame /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvaluateHandleAsync(string expression, object? arg = default); /// /// Use locator-based instead. Read more about locators. /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// focuses the element, fills it and triggers an input event after filling. + /// This method waits for an element matching , waits + /// for actionability + /// checks, focuses the element, fills it and triggers an input event after filling. /// Note that you can pass an empty string to clear the input field. /// /// @@ -461,9 +467,9 @@ public partial interface IFrame /// href="https://playwright.dev/dotnet/docs/locators">locators. /// /// - /// This method fetches an element with and focuses it. - /// If there's no element matching , the method waits until - /// a matching element appears in the DOM. + /// This method fetches an element with and focuses + /// it. If there's no element matching , the method waits + /// until a matching element appears in the DOM. /// /// /// @@ -800,7 +806,7 @@ public partial interface IFrame /// /// there's an SSL error (e.g. in case of self-signed certificates). /// target URL is invalid. - /// the is exceeded during navigation. + /// the is exceeded during navigation. /// the remote server does not respond or is unreachable. /// the main resource failed to load. /// @@ -809,6 +815,15 @@ public partial interface IFrame /// the remote server, including 404 "Not Found" and 500 "Internal Server Error". The /// status code for such responses can be retrieved by calling . /// + /// + /// The method either throws an error or returns a main resource response. The only + /// exceptions are navigation to about:blank or navigation to the same URL with + /// a different hash, which would succeed and return null. + /// + /// + /// Headless mode doesn't support navigation to a PDF document. See the upstream + /// issue. + /// /// /// /// @@ -831,27 +846,27 @@ public partial interface IFrame /// href="https://playwright.dev/dotnet/docs/locators">locators. /// /// - /// This method hovers over an element matching by performing + /// This method hovers over an element matching by performing /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is + /// set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// /// Use to hover over the center of the element, or the specified - /// . + /// . /// /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -976,7 +991,8 @@ public partial interface IFrame /// /// /// Returns whether the element is hidden, the opposite of visible. - /// that does not match any elements is considered hidden. + /// that does not match any elements is considered + /// hidden. /// /// /// @@ -993,7 +1009,8 @@ public partial interface IFrame /// /// /// Returns whether the element is visible. - /// that does not match any elements is considered not visible. + /// that does not match any elements is considered + /// not visible. /// /// /// @@ -1020,6 +1037,10 @@ public partial interface IFrame /// /// Returns frame's name attribute as specified in the tag. /// If the name is empty, returns the id attribute instead. + /// + /// This value is calculated once when the frame is created, and will not update if + /// the attribute is changed later. + /// /// /// /// @@ -1041,9 +1062,9 @@ public partial interface IFrame /// href="https://playwright.dev/dotnet/docs/locators">locators. /// /// - /// can specify the intended keyboardEvent.key - /// value or a single character to generate the text for. A superset of the values can be found here. + /// can specify the intended keyboardEvent.key + /// value or a single character to generate the text for. A superset of the + /// values can be found here. /// Examples of the keys are: /// /// @@ -1059,12 +1080,12 @@ public partial interface IFrame /// resolves to Control on Windows and Linux and to Meta on macOS. /// /// - /// Holding down Shift will type the text that corresponds to the + /// Holding down Shift will type the text that corresponds to the /// in the upper case. /// /// - /// If is a single character, it is case-sensitive, so the values - /// a and A will generate different respective texts. + /// If is a single character, it is case-sensitive, + /// so the values a and A will generate different respective texts. /// /// /// Shortcuts such as key: "Control+o", key: "Control++ or key: "Control+Shift+T" @@ -1087,6 +1108,10 @@ public partial interface IFrame /// Use locator-based instead. Read more about locators. /// Returns the ElementHandle pointing to the frame element. /// + /// The use of is discouraged, use + /// objects and web-first assertions instead. + /// + /// /// The method finds an element matching the specified selector within the frame. If /// no elements match the selector, returns null. /// @@ -1105,6 +1130,10 @@ public partial interface IFrame /// Use locator-based instead. Read more about locators. /// Returns the ElementHandles pointing to the frame elements. /// + /// The use of is discouraged, use + /// objects instead. + /// + /// /// The method finds all elements matching the specified selector within the frame. /// If no elements match the selector, returns empty array. /// @@ -1124,10 +1153,10 @@ public partial interface IFrame /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -1166,10 +1195,10 @@ public partial interface IFrame /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -1208,10 +1237,10 @@ public partial interface IFrame /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -1250,10 +1279,10 @@ public partial interface IFrame /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -1292,10 +1321,10 @@ public partial interface IFrame /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -1334,10 +1363,10 @@ public partial interface IFrame /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -1376,13 +1405,13 @@ public partial interface IFrame /// locators. /// /// - /// This method checks or unchecks an element matching by - /// performing the following steps: + /// This method checks or unchecks an element matching + /// by performing the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, + /// wait until a matching element is attached to the DOM. /// /// /// Ensure that matched element is a checkbox or a radio input. If not, this method @@ -1391,15 +1420,15 @@ public partial interface IFrame /// If the element already has the right checked state, this method returns immediately. /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option + /// is set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. /// Ensure that the element is now checked or unchecked. If not, this method throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -1433,7 +1462,7 @@ public partial interface IFrame /// For empty array, clears the selected files. /// /// - /// This method expects to point to an input + /// This method expects to point to an input /// element. However, if the element is inside the <label> element /// that has an associated control, /// targets the control instead. @@ -1459,7 +1488,7 @@ public partial interface IFrame /// For empty array, clears the selected files. /// /// - /// This method expects to point to an input + /// This method expects to point to an input /// element. However, if the element is inside the <label> element /// that has an associated control, /// targets the control instead. @@ -1485,7 +1514,7 @@ public partial interface IFrame /// For empty array, clears the selected files. /// /// - /// This method expects to point to an input + /// This method expects to point to an input /// element. However, if the element is inside the <label> element /// that has an associated control, /// targets the control instead. @@ -1511,7 +1540,7 @@ public partial interface IFrame /// For empty array, clears the selected files. /// /// - /// This method expects to point to an input + /// This method expects to point to an input /// element. However, if the element is inside the <label> element /// that has an associated control, /// targets the control instead. @@ -1529,30 +1558,34 @@ public partial interface IFrame /// /// Use locator-based instead. Read more about locators. /// - /// This method taps an element matching by performing the - /// following steps: + /// This method taps an element matching by performing + /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until + /// Find an element matching . If there is none, wait until /// a matching element is attached to the DOM. /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is set. + /// If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// /// Use to tap the center of the element, or the specified - /// . + /// . /// /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// + /// + /// frame.tap() requires that the hasTouch option of the browser context + /// be set to true. + /// /// /// /// @@ -1613,13 +1646,13 @@ public partial interface IFrame /// href="https://playwright.dev/dotnet/docs/locators">locators. /// /// - /// This method checks an element matching by performing + /// This method checks an element matching by performing /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Ensure that matched element is a checkbox or a radio input. If not, this method @@ -1627,15 +1660,15 @@ public partial interface IFrame /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is + /// set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. /// Ensure that the element is now unchecked. If not, this method throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -1652,8 +1685,8 @@ public partial interface IFrame /// /// - /// Returns when the returns a truthy value, returns that - /// value. + /// Returns when the returns a truthy value, + /// returns that value. /// /// **Usage** /// @@ -1686,7 +1719,7 @@ public partial interface IFrame /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . /// Call options Task WaitForFunctionAsync(string expression, object? arg = default, FrameWaitForFunctionOptions? options = default); @@ -1697,6 +1730,10 @@ public partial interface IFrame /// The navigation must have been committed when this method is called. If current document /// has already reached the required state, resolves immediately. /// + /// + /// Most of the time, this method is not needed because Playwright auto-waits + /// before every action. + /// /// **Usage** /// /// await frame.ClickAsync("button");
@@ -1751,6 +1788,10 @@ public partial interface IFrame ///
/// // Resolves after navigation has finished ///
+ /// + /// Usage of the History + /// API to change the URL is considered a navigation. + /// ///
/// /// @@ -1787,6 +1828,10 @@ public partial interface IFrame ///
/// // Resolves after navigation has finished ///
+ /// + /// Usage of the History + /// API to change the URL is considered a navigation. + /// /// /// /// @@ -1805,15 +1850,20 @@ public partial interface IFrame /// instead. Read more about locators. /// /// - /// Returns when element specified by selector satisfies option. - /// Returns null if waiting for hidden or detached. + /// Returns when element specified by selector satisfies + /// option. Returns null if waiting for hidden or detached. + /// + /// + /// Playwright automatically waits for element to be ready before performing an action. + /// Using objects and web-first assertions make the code wait-for-selector-free. /// /// - /// Wait for the to satisfy option - /// (either appear/disappear from dom, or become visible/hidden). If at the moment of - /// calling the method already satisfies the condition, - /// the method will return immediately. If the selector doesn't satisfy the condition - /// for the milliseconds, the function will throw. + /// Wait for the to satisfy + /// option (either appear/disappear from dom, or become visible/hidden). If at the moment + /// of calling the method already satisfies + /// the condition, the method will return immediately. If the selector doesn't satisfy + /// the condition for the milliseconds, the + /// function will throw. /// /// **Usage** /// This method works across navigations: @@ -1844,6 +1894,7 @@ public partial interface IFrame /// /// Playwright automatically waits for element to be ready before performing an action. /// Using objects and web-first assertions make the code wait-for-selector-free. + /// /// /// /// A selector to query for. @@ -1855,7 +1906,7 @@ public partial interface IFrame /// Never wait for timeout in production. Tests that wait for time are inherently flaky. /// Use actions and web assertions that wait automatically. /// - /// Waits for the given in milliseconds. + /// Waits for the given in milliseconds. /// /// Note that frame.waitForTimeout() should only be used for debugging. Tests /// using the timer in production are going to be flaky. Use signals such as network diff --git a/src/Playwright/API/Generated/IFrameLocator.cs b/src/Playwright/API/Generated/IFrameLocator.cs index 352468b468..97189aeee4 100644 --- a/src/Playwright/API/Generated/IFrameLocator.cs +++ b/src/Playwright/API/Generated/IFrameLocator.cs @@ -32,11 +32,11 @@ namespace Microsoft.Playwright; /// /// FrameLocator represents a view to the iframe on the page. It captures the /// logic sufficient to retrieve the iframe and locate elements in that iframe. -/// FrameLocator can be created with either or method. +/// FrameLocator can be created with either , or method. /// /// -/// var locator = page.FrameLocator("#my-frame").GetByText("Submit");
+/// var locator = page.Locator("#my-frame").ContentFrame.GetByText("Submit");
/// await locator.ClickAsync(); ///
/// **Strictness** @@ -46,10 +46,10 @@ namespace Microsoft.Playwright; ///
/// /// // Throws if there are several frames in DOM:
-/// await page.FrameLocator(".result-frame").GetByRole(AriaRole.Button).ClickAsync();
+/// await page.Locator(".result-frame").ContentFrame.GetByRole(AriaRole.Button).ClickAsync();
///
/// // Works because we explicitly tell locator to pick the first frame:
-/// await page.FrameLocator(".result-frame").First.getByRole(AriaRole.Button).ClickAsync(); +/// await page.Locator(".result-frame").First.ContentFrame.getByRole(AriaRole.Button).ClickAsync(); ///
/// **Converting Locator to FrameLocator** /// @@ -65,6 +65,7 @@ namespace Microsoft.Playwright; public partial interface IFrameLocator { /// Returns locator to the first matching frame. + [System.Obsolete] IFrameLocator First { get; } /// @@ -340,6 +341,7 @@ public partial interface IFrameLocator ILocator GetByTitle(Regex text, FrameLocatorGetByTitleOptions? options = default); /// Returns locator to the last matching frame. + [System.Obsolete] IFrameLocator Last { get; } /// @@ -366,12 +368,17 @@ public partial interface IFrameLocator /// /// + /// **DEPRECATED** Use followed by + /// instead. + /// + /// /// Returns locator to the n-th matching frame. It's zero based, nth(0) selects /// the first frame. /// /// /// /// + [System.Obsolete] IFrameLocator Nth(int index); /// @@ -386,7 +393,7 @@ public partial interface IFrameLocator /// For a reverse operation, use . /// **Usage** /// - /// var frameLocator = Page.FrameLocator("iframe[name=\"embedded\"]");
+ /// var frameLocator = Page.Locator("iframe[name=\"embedded\"]").ContentFrame;
/// // ...
/// var locator = frameLocator.Owner;
/// await Expect(locator).ToBeVisibleAsync(); diff --git a/src/Playwright/API/Generated/IJSHandle.cs b/src/Playwright/API/Generated/IJSHandle.cs index 31c95994a7..99dbd23d26 100644 --- a/src/Playwright/API/Generated/IJSHandle.cs +++ b/src/Playwright/API/Generated/IJSHandle.cs @@ -56,10 +56,10 @@ public partial interface IJSHandle IElementHandle? AsElement(); ///
- /// Returns the return value of . - /// This method passes this handle as the first argument to . + /// Returns the return value of . + /// This method passes this handle as the first argument to . /// - /// If returns a , then handle.evaluate + /// If returns a , then handle.evaluate /// would wait for the promise to resolve and return its value. /// /// **Usage** @@ -72,12 +72,15 @@ public partial interface IJSHandle /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvaluateAsync(string expression, object? arg = default); /// - /// Returns the return value of as a . - /// This method passes this handle as the first argument to . + /// + /// Returns the return value of as a . + /// + /// This method passes this handle as the first argument to . /// /// The only difference between jsHandle.evaluate and jsHandle.evaluateHandle /// is that jsHandle.evaluateHandle returns . @@ -93,7 +96,7 @@ public partial interface IJSHandle /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvaluateHandleAsync(string expression, object? arg = default); /// @@ -121,6 +124,10 @@ public partial interface IJSHandle /// Returns a JSON representation of the object. If the object has a toJSON function, /// it **will not be called**. /// + /// + /// The method will return an empty JSON object if the referenced object is not stringifiable. + /// It will throw an error if the object has circular references. + /// /// /// /// diff --git a/src/Playwright/API/Generated/IKeyboard.cs b/src/Playwright/API/Generated/IKeyboard.cs index c207e85dc3..a5b85e915d 100644 --- a/src/Playwright/API/Generated/IKeyboard.cs +++ b/src/Playwright/API/Generated/IKeyboard.cs @@ -72,9 +72,9 @@ public partial interface IKeyboard /// /// Dispatches a keydown event. /// - /// can specify the intended keyboardEvent.key - /// value or a single character to generate the text for. A superset of the values can be found here. + /// can specify the intended keyboardEvent.key + /// value or a single character to generate the text for. A superset of the + /// values can be found here. /// Examples of the keys are: /// /// @@ -90,23 +90,27 @@ public partial interface IKeyboard /// resolves to Control on Windows and Linux and to Meta on macOS. /// /// - /// Holding down Shift will type the text that corresponds to the + /// Holding down Shift will type the text that corresponds to the /// in the upper case. /// /// - /// If is a single character, it is case-sensitive, so the values - /// a and A will generate different respective texts. + /// If is a single character, it is case-sensitive, + /// so the values a and A will generate different respective texts. /// /// - /// If is a modifier key, Shift, Meta, Control, - /// or Alt, subsequent key presses will be sent with that modifier active. To - /// release the modifier key, use . + /// If is a modifier key, Shift, Meta, + /// Control, or Alt, subsequent key presses will be sent with that modifier + /// active. To release the modifier key, use . /// /// /// After the key is pressed once, subsequent calls to /// will have repeat /// set to true. To release the key, use . /// + /// + /// Modifier keys DO influence keyboard.down. Holding down Shift will + /// type the text in upper case. + /// /// /// /// @@ -127,6 +131,10 @@ public partial interface IKeyboard /// /// **Usage** /// await page.Keyboard.PressAsync("嗨"); + /// + /// Modifier keys DO NOT effect keyboard.insertText. Holding down Shift + /// will not type the text in upper case. + /// /// /// /// @@ -138,10 +146,11 @@ public partial interface IKeyboard Task InsertTextAsync(string text); /// + /// In most cases, you should use instead. /// - /// can specify the intended keyboardEvent.key - /// value or a single character to generate the text for. A superset of the values can be found here. + /// can specify the intended keyboardEvent.key + /// value or a single character to generate the text for. A superset of the + /// values can be found here. /// Examples of the keys are: /// /// @@ -157,12 +166,12 @@ public partial interface IKeyboard /// resolves to Control on Windows and Linux and to Meta on macOS. /// /// - /// Holding down Shift will type the text that corresponds to the + /// Holding down Shift will type the text that corresponds to the /// in the upper case. /// /// - /// If is a single character, it is case-sensitive, so the values - /// a and A will generate different respective texts. + /// If is a single character, it is case-sensitive, + /// so the values a and A will generate different respective texts. /// /// /// Shortcuts such as key: "Control+o", key: "Control++ or key: "Control+Shift+T" @@ -192,6 +201,11 @@ public partial interface IKeyboard /// /// + /// In most cases, you should use instead. You only + /// need to press keys one by one if there is special keyboard handling on the page + /// - in this case use . + /// + /// /// Sends a keydown, keypress/input, and keyup event for /// each character in the text. /// @@ -201,6 +215,14 @@ public partial interface IKeyboard /// await page.Keyboard.TypeAsync("Hello"); // types instantly
/// await page.Keyboard.TypeAsync("World", new() { Delay = 100 }); // types slower, like a user ///
+ /// + /// Modifier keys DO NOT effect keyboard.type. Holding down Shift will + /// not type the text in upper case. + /// + /// + /// For characters that are not on a US keyboard, only an input event will be + /// sent. + /// ///
/// /// diff --git a/src/Playwright/API/Generated/ILocator.cs b/src/Playwright/API/Generated/ILocator.cs index 6d0cb623eb..ce1d82e585 100644 --- a/src/Playwright/API/Generated/ILocator.cs +++ b/src/Playwright/API/Generated/ILocator.cs @@ -46,6 +46,18 @@ public partial interface ILocator /// When the locator points to a list of elements, this returns an array of locators, /// pointing to their respective elements. /// + /// + /// does not wait for elements to match the locator, + /// and instead immediately returns whatever is present in the page. + /// + /// + /// When the list of elements changes dynamically, will + /// produce unpredictable and flaky results. + /// + /// + /// When the list of elements is stable, but loaded dynamically, wait for the full list + /// to finish loading before calling . + /// /// **Usage** /// /// foreach (var li in await page.GetByRole("listitem").AllAsync())
@@ -59,26 +71,39 @@ public partial interface ILocator /// of elements changes dynamically, will produce unpredictable /// and flaky results. When the list of elements is stable, but loaded dynamically, /// wait for the full list to finish loading before calling . + /// /// ///
Task> AllAsync(); /// /// Returns an array of node.innerText values for all matching nodes. + /// + /// If you need to assert text on the page, prefer + /// with option to avoid flakiness. + /// See assertions guide + /// for more details. + /// /// **Usage** /// var texts = await page.GetByRole(AriaRole.Link).AllInnerTextsAsync(); /// /// /// /// If you need to assert text on the page, prefer - /// with option to avoid flakiness. See assertions - /// guide for more details. + /// with option to avoid flakiness. + /// See assertions guide + /// for more details. /// /// Task> AllInnerTextsAsync(); /// /// Returns an array of node.textContent values for all matching nodes. + /// + /// If you need to assert text on the page, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// /// **Usage** /// var texts = await page.GetByRole(AriaRole.Link).AllTextContentsAsync(); /// @@ -148,7 +173,7 @@ public partial interface ILocator /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is set. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. @@ -159,7 +184,7 @@ public partial interface ILocator /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -196,15 +221,15 @@ public partial interface ILocator /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is set. /// /// Scroll the element into view if needed. /// /// Use to click in the center of the element, or the specified - /// . + /// . /// /// - /// Wait for initiated navigations to either succeed or fail, unless + /// Wait for initiated navigations to either succeed or fail, unless /// option is set. /// /// @@ -213,7 +238,7 @@ public partial interface ILocator /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -234,6 +259,11 @@ public partial interface ILocator /// /// Returns the number of elements matching the locator. + /// + /// If you need to assert the number of elements on the page, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// /// **Usage** /// int count = await page.GetByRole(AriaRole.Listitem).CountAsync(); /// @@ -253,12 +283,12 @@ public partial interface ILocator /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is set. /// /// Scroll the element into view if needed. /// /// Use to double click in the center of the element, or the - /// specified . + /// specified . /// /// /// @@ -266,10 +296,14 @@ public partial interface ILocator /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// + /// + /// element.dblclick() dispatches two click events and a single dblclick + /// event. + /// ///
/// /// @@ -291,13 +325,13 @@ public partial interface ILocator /// to calling element.click(). /// /// - /// Under the hood, it creates an instance of an event based on the given , initializes it with properties and dispatches + /// Under the hood, it creates an instance of an event based on the given , + /// initializes it with properties and dispatches /// it on the element. Events are composed, cancelable and bubble by default. /// /// - /// Since is event-specific, please refer to the events - /// documentation for the lists of initial properties: + /// Since is event-specific, please refer + /// to the events documentation for the lists of initial properties: /// /// /// DeviceMotionEvent @@ -403,14 +437,15 @@ public partial interface ILocator /// Execute JavaScript code in the page, taking the matching element as an argument. /// **Details** /// - /// Returns the return value of , called with the matching - /// element as a first argument, and as a second argument. + /// Returns the return value of , called with the + /// matching element as a first argument, and as + /// a second argument. /// /// - /// If returns a , this method will - /// wait for the promise to resolve and return its value. + /// If returns a , this method + /// will wait for the promise to resolve and return its value. /// - /// If throws or rejects, this method throws. + /// If throws or rejects, this method throws. /// **Usage** /// /// var tweets = page.Locator(".tweet .retweets");
@@ -421,7 +456,7 @@ public partial interface ILocator /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . /// Call options Task EvaluateAsync(string expression, object? arg = default, LocatorEvaluateOptions? options = default); @@ -429,15 +464,15 @@ public partial interface ILocator /// Execute JavaScript code in the page, taking all matching elements as an argument. /// **Details** /// - /// Returns the return value of , called with an array - /// of all matching elements as a first argument, and as a second - /// argument. + /// Returns the return value of , called with + /// an array of all matching elements as a first argument, and + /// as a second argument. /// /// - /// If returns a , this method will - /// wait for the promise to resolve and return its value. + /// If returns a , this method + /// will wait for the promise to resolve and return its value. /// - /// If throws or rejects, this method throws. + /// If throws or rejects, this method throws. /// **Usage** /// /// var locator = page.Locator("div");
@@ -448,7 +483,7 @@ public partial interface ILocator /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvaluateAllAsync(string expression, object? arg = default); /// @@ -458,26 +493,26 @@ public partial interface ILocator /// /// **Details** /// - /// Returns the return value of as a, - /// called with the matching element as a first argument, and - /// as a second argument. + /// Returns the return value of as a, called with the matching element as a first argument, and as a second argument. /// /// /// The only difference between and /// is that returns . /// /// - /// If returns a , this method will - /// wait for the promise to resolve and return its value. + /// If returns a , this + /// method will wait for the promise to resolve and return its value. /// - /// If throws or rejects, this method throws. + /// If throws or rejects, this method throws. /// See for more details. /// /// /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . /// Call options Task EvaluateHandleAsync(string expression, object? arg = default, LocatorEvaluateHandleOptions? options = default); @@ -552,7 +587,14 @@ public partial interface ILocator /// A selector to use when resolving DOM element. IFrameLocator FrameLocator(string selector); - /// Returns the matching element's attribute value. + /// + /// Returns the matching element's attribute value. + /// + /// If you need to assert an element's attribute, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// + /// /// /// /// If you need to assert an element's attribute, prefer @@ -844,12 +886,12 @@ public partial interface ILocator /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is set. /// /// Scroll the element into view if needed. /// /// Use to hover over the center of the element, or the specified - /// . + /// . /// /// /// @@ -857,7 +899,7 @@ public partial interface ILocator /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -869,12 +911,21 @@ public partial interface ILocator /// Call options Task InnerHTMLAsync(LocatorInnerHTMLOptions? options = default); - /// Returns the element.innerText. + /// + /// Returns the element.innerText. + /// + /// If you need to assert text on the page, prefer + /// with option to avoid flakiness. + /// See assertions guide + /// for more details. + /// + /// /// /// /// If you need to assert text on the page, prefer - /// with option to avoid flakiness. See assertions - /// guide for more details. + /// with option to avoid flakiness. + /// See assertions guide + /// for more details. /// /// /// Call options @@ -885,6 +936,11 @@ public partial interface ILocator /// Returns the value for the matching <input> or <textarea> /// or <select> element. /// + /// + /// If you need to assert input value, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// /// **Usage** /// String value = await page.GetByRole(AriaRole.Textbox).InputValueAsync(); /// **Details** @@ -909,6 +965,11 @@ public partial interface ILocator /// Returns whether the element is checked. Throws if the element is not a checkbox /// or radio input. /// + /// + /// If you need to assert that checkbox is checked, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// /// **Usage** /// var isChecked = await page.GetByRole(AriaRole.Checkbox).IsCheckedAsync(); ///
@@ -924,6 +985,11 @@ public partial interface ILocator /// /// Returns whether the element is disabled, the opposite of enabled. + /// + /// If you need to assert that an element is disabled, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// /// **Usage** /// Boolean disabled = await page.GetByRole(AriaRole.Button).IsDisabledAsync(); /// @@ -939,6 +1005,11 @@ public partial interface ILocator /// /// Returns whether the element is editable. + /// + /// If you need to assert that an element is editable, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// /// **Usage** /// Boolean editable = await page.GetByRole(AriaRole.Textbox).IsEditableAsync(); /// @@ -954,6 +1025,11 @@ public partial interface ILocator /// /// Returns whether the element is enabled. + /// + /// If you need to assert that an element is enabled, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// /// **Usage** /// Boolean enabled = await page.GetByRole(AriaRole.Button).IsEnabledAsync(); /// @@ -969,6 +1045,11 @@ public partial interface ILocator /// /// Returns whether the element is hidden, the opposite of visible. + /// + /// If you need to assert that element is hidden, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// /// **Usage** /// Boolean hidden = await page.GetByRole(AriaRole.Button).IsHiddenAsync(); /// @@ -984,6 +1065,11 @@ public partial interface ILocator /// /// Returns whether the element is visible. + /// + /// If you need to assert that element is visible, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// /// **Usage** /// Boolean visible = await page.GetByRole(AriaRole.Button).IsVisibleAsync(); /// @@ -1039,7 +1125,12 @@ public partial interface ILocator ILocator Nth(int index); /// - /// Creates a locator that matches either of the two locators. + /// Creates a locator matching all elements that match one or both of the two locators. + /// + /// Note that when both locators match something, the resulting locator will have multiple + /// matches and violate locator + /// strictness guidelines. + /// /// **Usage** /// /// Consider a scenario where you'd like to click on a "New email" button, but sometimes @@ -1068,9 +1159,9 @@ public partial interface ILocator /// **Details** /// Focuses the element, and then uses and . /// - /// can specify the intended keyboardEvent.key - /// value or a single character to generate the text for. A superset of the values can be found here. + /// can specify the intended keyboardEvent.key + /// value or a single character to generate the text for. A superset of the + /// values can be found here. /// Examples of the keys are: /// /// @@ -1086,12 +1177,12 @@ public partial interface ILocator /// resolves to Control on Windows and Linux and to Meta on macOS. /// /// - /// Holding down Shift will type the text that corresponds to the + /// Holding down Shift will type the text that corresponds to the /// in the upper case. /// /// - /// If is a single character, it is case-sensitive, so the values - /// a and A will generate different respective texts. + /// If is a single character, it is case-sensitive, + /// so the values a and A will generate different respective texts. /// /// /// Shortcuts such as key: "Control+o", key: "Control++ or key: "Control+Shift+T" @@ -1108,6 +1199,10 @@ public partial interface ILocator /// /// + /// In most cases, you should use instead. You only + /// need to press keys one by one if there is special keyboard handling on the page. + /// + /// /// Focuses the element, and then sends a keydown, keypress/input, /// and keyup event for each character in the text. /// @@ -1128,6 +1223,7 @@ public partial interface ILocator /// /// In most cases, you should use instead. You only /// need to press keys one by one if there is special keyboard handling on the page. + /// /// /// /// String of characters to sequentially press into a focused element. @@ -1434,15 +1530,15 @@ public partial interface ILocator /// If the element already has the right checked state, this method returns immediately. /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option + /// is set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. /// Ensure that the element is now checked or unchecked. If not, this method throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -1638,12 +1734,12 @@ public partial interface ILocator /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is set. /// /// Scroll the element into view if needed. /// /// Use to tap the center of the element, or the specified - /// . + /// . /// /// /// @@ -1651,10 +1747,14 @@ public partial interface ILocator /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// + /// + /// element.tap() requires that the hasTouch option of the browser context + /// be set to true. + /// /// /// /// @@ -1665,7 +1765,14 @@ public partial interface ILocator /// Call options Task TapAsync(LocatorTapOptions? options = default); - /// Returns the node.textContent. + /// + /// Returns the node.textContent. + /// + /// If you need to assert text on the page, prefer + /// to avoid flakiness. See assertions + /// guide for more details. + /// + /// /// /// /// If you need to assert text on the page, prefer @@ -1707,7 +1814,7 @@ public partial interface ILocator /// /// /// Wait for actionability - /// checks on the element, unless option is set. + /// checks on the element, unless option is set. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. @@ -1718,7 +1825,7 @@ public partial interface ILocator /// throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -1728,13 +1835,13 @@ public partial interface ILocator /// /// - /// Returns when element specified by locator satisfies the + /// Returns when element specified by locator satisfies the /// option. /// /// /// If target element already satisfies the condition, the method returns immediately. - /// Otherwise, waits for up to milliseconds until the condition - /// is met. + /// Otherwise, waits for up to milliseconds until + /// the condition is met. /// /// **Usage** /// diff --git a/src/Playwright/API/Generated/IMouse.cs b/src/Playwright/API/Generated/IMouse.cs index b31997a8e3..bdb5b9ce58 100644 --- a/src/Playwright/API/Generated/IMouse.cs +++ b/src/Playwright/API/Generated/IMouse.cs @@ -88,6 +88,10 @@ public partial interface IMouse /// the page. See scrolling /// for alternative ways to scroll. /// + /// + /// Wheel events may cause scrolling if they are not handled, and this method does not + /// wait for the scrolling to finish before returning. + /// /// /// /// diff --git a/src/Playwright/API/Generated/IPage.cs b/src/Playwright/API/Generated/IPage.cs index f8152e191b..f662dc8cf4 100644 --- a/src/Playwright/API/Generated/IPage.cs +++ b/src/Playwright/API/Generated/IPage.cs @@ -136,6 +136,10 @@ public partial interface IPage /// Console.WriteLine(request.Url + " " + request.Failure);
/// }; ///
+ /// + /// When no or listeners + /// are present, all dialogs are automatically dismissed. + /// ///
/// /// @@ -226,6 +230,10 @@ public partial interface IPage /// });
/// Console.WriteLine(await popup.EvaluateAsync<string>("location.href")); ///
+ /// + /// Use to wait until the page gets to a particular + /// state (you should not need it in most cases). + /// ///
/// /// @@ -244,7 +252,16 @@ public partial interface IPage ///
event EventHandler Request; - /// Emitted when a request fails, for example by timing out. + /// + /// Emitted when a request fails, for example by timing out. + /// + /// HTTP Error responses, such as 404 or 503, are still successful responses from HTTP + /// standpoint, so request will complete with event + /// and not with . A request will only be considered + /// failed when the client cannot get an HTTP response from the server, e.g. due to + /// network error net::ERR_FAILED. + /// + /// /// /// /// HTTP Error responses, such as 404 or 503, are still successful responses from HTTP @@ -312,6 +329,10 @@ public partial interface IPage /// **Usage** /// An example of overriding Math.random before the page loads: /// await Page.AddInitScriptAsync(scriptPath: "./preload.js"); + /// + /// The order of evaluation of multiple scripts installed via + /// and is not defined. + /// ///
/// /// @@ -353,13 +374,13 @@ public partial interface IPage /// href="https://playwright.dev/dotnet/docs/locators">locators. /// /// - /// This method checks an element matching by performing + /// This method checks an element matching by performing /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Ensure that matched element is a checkbox or a radio input. If not, this method @@ -367,15 +388,15 @@ public partial interface IPage /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is set. + /// If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. /// Ensure that the element is now checked. If not, this method throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -393,31 +414,31 @@ public partial interface IPage /// href="https://playwright.dev/dotnet/docs/locators">locators. ///
/// - /// This method clicks an element matching by performing + /// This method clicks an element matching by performing /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is set. + /// If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// /// Use to click in the center of the element, or the specified - /// . + /// . /// /// - /// Wait for initiated navigations to either succeed or fail, unless + /// Wait for initiated navigations to either succeed or fail, unless /// option is set. /// /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -431,15 +452,20 @@ public partial interface IPage /// /// - /// If is false, does not run any unload handlers - /// and waits for the page to be closed. If is true + /// If is false, does not run any unload handlers + /// and waits for the page to be closed. If is true /// the method will run unload handlers, but will **not** wait for the page to close. /// /// By default, page.close() **does not** run beforeunload handlers. + /// + /// if is passed as true, a beforeunload dialog + /// might be summoned and should be handled manually via + /// event. + /// /// /// /// - /// if is passed as true, a beforeunload dialog + /// if is passed as true, a beforeunload dialog /// might be summoned and should be handled manually via /// event. /// @@ -459,30 +485,34 @@ public partial interface IPage /// locators. /// /// - /// This method double clicks an element matching by performing - /// the following steps: + /// This method double clicks an element matching + /// by performing the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is + /// set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// /// Use to double click in the center of the element, or the - /// specified . + /// specified . /// /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// + /// + /// page.dblclick() dispatches two click events and a single dblclick + /// event. + /// /// /// /// @@ -510,13 +540,13 @@ public partial interface IPage /// **Usage** /// await page.DispatchEventAsync("button#submit", "click"); /// - /// Under the hood, it creates an instance of an event based on the given , initializes it with properties and dispatches + /// Under the hood, it creates an instance of an event based on the given , + /// initializes it with properties and dispatches /// it on the element. Events are composed, cancelable and bubble by default. /// /// - /// Since is event-specific, please refer to the events - /// documentation for the lists of initial properties: + /// Since is event-specific, please refer to + /// the events documentation for the lists of initial properties: /// /// /// DeviceMotionEvent @@ -622,12 +652,14 @@ public partial interface IPage /// /// /// The method finds an element matching the specified selector within the page and - /// passes it as a first argument to . If no elements match - /// the selector, the method throws an error. Returns the value of . + /// passes it as a first argument to . If no + /// elements match the selector, the method throws an error. Returns the value of . /// /// - /// If returns a , then - /// would wait for the promise to resolve and return its value. + /// If returns a , then would wait for the promise to resolve and return + /// its value. /// /// **Usage** /// @@ -641,7 +673,7 @@ public partial interface IPage /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . /// Call options Task EvalOnSelectorAsync(string selector, string expression, object? arg = default, PageEvalOnSelectorOptions? options = default); @@ -652,12 +684,13 @@ public partial interface IPage /// /// /// The method finds all elements matching the specified selector within the page and - /// passes an array of matched elements as a first argument to . - /// Returns the result of invocation. + /// passes an array of matched elements as a first argument to . + /// Returns the result of invocation. /// /// - /// If returns a , then - /// would wait for the promise to resolve and return its value. + /// If returns a , then + /// would wait for the promise to resolve + /// and return its value. /// /// **Usage** /// var divsCount = await page.EvalOnSelectorAllAsync<bool>("div", "(divs, min) => divs.length >= min", 10); @@ -667,11 +700,11 @@ public partial interface IPage /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvalOnSelectorAllAsync(string selector, string expression, object? arg = default); /// - /// Returns the value of the invocation. + /// Returns the value of the invocation. /// /// If the function passed to the returns a , /// then would wait for the promise to resolve and @@ -684,7 +717,7 @@ public partial interface IPage /// are not serializable by JSON: -0, NaN, Infinity, -Infinity. /// /// **Usage** - /// Passing argument to : + /// Passing argument to : /// /// var result = await page.EvaluateAsync<int>("([x, y]) => Promise.resolve(x * y)", new[] { 7, 8 });
/// Console.WriteLine(result); @@ -705,11 +738,14 @@ public partial interface IPage /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvaluateAsync(string expression, object? arg = default); /// - /// Returns the value of the invocation as a . + /// + /// Returns the value of the invocation as a + /// . + /// /// /// The only difference between and /// is that returns . @@ -738,23 +774,24 @@ public partial interface IPage /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvaluateHandleAsync(string expression, object? arg = default); /// /// - /// The method adds a function called on the window object - /// of every frame in this page. When called, the function executes - /// and returns a which resolves to the return value of . If the returns a , - /// it will be awaited. + /// The method adds a function called on the + /// window object of every frame in this page. When called, the function executes + /// and returns a which resolves + /// to the return value of . If the + /// returns a , it will be awaited. /// /// - /// The first argument of the function contains information - /// about the caller: { browserContext: BrowserContext, page: Page, frame: Frame - /// }. + /// The first argument of the function contains + /// information about the caller: { browserContext: BrowserContext, page: Page, frame: + /// Frame }. /// /// See for the context-wide version. + /// Functions installed via survive navigations. /// **Usage** /// An example of exposing page URL to all frames in a page: /// @@ -786,7 +823,12 @@ public partial interface IPage /// } /// /// - /// Functions installed via survive navigations. + /// + /// + /// Functions installed via survive navigations. + /// + /// + /// /// Name of the function on the window object. /// Callback function that will be called in the Playwright's context. /// Call options @@ -794,13 +836,17 @@ public partial interface IPage /// /// - /// The method adds a function called on the window object - /// of every frame in the page. When called, the function executes - /// and returns a which resolves to the return value of . + /// The method adds a function called on the + /// window object of every frame in the page. When called, the function executes + /// and returns a which resolves + /// to the return value of . + /// + /// + /// If the returns a , it + /// will be awaited. /// - /// If the returns a , it will be awaited. /// See for context-wide exposed function. + /// Functions installed via survive navigations. /// **Usage** /// An example of adding a sha256 function to the page: /// @@ -840,7 +886,12 @@ public partial interface IPage /// } /// /// - /// Functions installed via survive navigations. + /// + /// + /// Functions installed via survive navigations. + /// + /// + /// /// Name of the function on the window object /// Callback function which will be called in Playwright's context. Task ExposeFunctionAsync(string name, Action callback); @@ -848,7 +899,7 @@ public partial interface IPage /// /// Use locator-based instead. Read more about locators. /// - /// This method waits for an element matching , waits for + /// This method waits for an element matching , waits for /// actionability checks, /// focuses the element, fills it and triggers an input event after filling. /// Note that you can pass an empty string to clear the input field. @@ -878,9 +929,9 @@ public partial interface IPage /// href="https://playwright.dev/dotnet/docs/locators">locators. /// /// - /// This method fetches an element with and focuses it. - /// If there's no element matching , the method waits until - /// a matching element appears in the DOM. + /// This method fetches an element with and focuses it. + /// If there's no element matching , the method waits + /// until a matching element appears in the DOM. /// /// /// @@ -1244,6 +1295,27 @@ public partial interface IPage /// Call options Task GoForwardAsync(PageGoForwardOptions? options = default); + /// + /// + /// Request the page to perform garbage collection. Note that there is no guarantee + /// that all unreachable objects will be collected. + /// + /// + /// This is useful to help detect memory leaks. For example, if your page has a large + /// object 'suspect' that might be leaked, you can check that it does not leak + /// by using a WeakRef. + /// + /// + /// // 1. In your page, save a WeakRef for the "suspect".
+ /// await Page.EvaluateAsync("globalThis.suspectWeakRef = new WeakRef(suspect)");
+ /// // 2. Request garbage collection.
+ /// await Page.RequestGCAsync();
+ /// // 3. Check that weak ref does not deref to the original object.
+ /// Assert.True(await Page.EvaluateAsync("!globalThis.suspectWeakRef.deref()")); + ///
+ ///
+ Task RequestGCAsync(); + /// /// /// Returns the main resource response. In case of multiple redirects, the navigation @@ -1253,7 +1325,7 @@ public partial interface IPage /// /// there's an SSL error (e.g. in case of self-signed certificates). /// target URL is invalid. - /// the is exceeded during navigation. + /// the is exceeded during navigation. /// the remote server does not respond or is unreachable. /// the main resource failed to load. /// @@ -1262,6 +1334,15 @@ public partial interface IPage /// the remote server, including 404 "Not Found" and 500 "Internal Server Error". The /// status code for such responses can be retrieved by calling . /// + /// + /// The method either throws an error or returns a main resource response. The only + /// exceptions are navigation to about:blank or navigation to the same URL with + /// a different hash, which would succeed and return null. + /// + /// + /// Headless mode doesn't support navigation to a PDF document. See the upstream + /// issue. + /// /// /// /// @@ -1276,8 +1357,8 @@ public partial interface IPage /// /// /// URL to navigate page to. The url should include scheme, e.g. https://. When - /// a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// a via the context options was provided and + /// the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -1289,27 +1370,27 @@ public partial interface IPage /// href="https://playwright.dev/dotnet/docs/locators">locators. ///
/// - /// This method hovers over an element matching by performing + /// This method hovers over an element matching by performing /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is set. + /// If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// /// Use to hover over the center of the element, or the specified - /// . + /// . /// /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -1440,7 +1521,8 @@ public partial interface IPage /// /// /// Returns whether the element is hidden, the opposite of visible. - /// that does not match any elements is considered hidden. + /// that does not match any elements is considered + /// hidden. /// ///
/// @@ -1457,7 +1539,8 @@ public partial interface IPage /// /// /// Returns whether the element is visible. - /// that does not match any elements is considered not visible. + /// that does not match any elements is considered + /// not visible. /// ///
/// @@ -1510,22 +1593,32 @@ public partial interface IPage /// User can inspect selectors or perform manual steps while paused. Resume will continue /// running the original script from the place it was paused. /// + /// + /// This method requires Playwright to be started in a headed mode, with a falsy option. + /// /// /// /// - /// This method requires Playwright to be started in a headed mode, with a falsy value in the . + /// This method requires Playwright to be started in a headed mode, with a falsy option. /// /// Task PauseAsync(); /// /// Returns the PDF buffer. + /// Generating a pdf is currently only supported in Chromium headless. /// /// page.pdf() generates a pdf of the page with print css media. To generate /// a pdf with screen media, call before /// calling page.pdf(): /// + /// + /// By default, page.pdf() generates a pdf with modified colors for printing. + /// Use the -webkit-print-color-adjust + /// property to force rendering of exact colors. + /// /// **Usage** /// /// // Generates a PDF with 'screen' media type
@@ -1533,7 +1626,7 @@ public partial interface IPage /// await page.PdfAsync(new() { Path = "page.pdf" }); ///
/// - /// The , , and + /// The , , and /// options accept values labeled with units. Unlabeled values are treated as pixels. /// /// A few examples: @@ -1549,7 +1642,7 @@ public partial interface IPage /// cm - centimeter /// mm - millimeter /// - /// The options are: + /// The options are: /// /// Letter: 8.5in x 11in /// Legal: 8.5in x 14in @@ -1563,6 +1656,11 @@ public partial interface IPage /// A5: 5.83in x 8.27in /// A6: 4.13in x 5.83in /// + /// + /// and markup have the following + /// limitations: > 1. Script tags inside templates are not evaluated. > 2. Page styles + /// are not visible inside templates. + /// ///
/// /// Generating a pdf is currently only supported in Chromium headless. @@ -1572,9 +1670,9 @@ public partial interface IPage /// property to force rendering of exact colors. /// /// - /// and markup have - /// the following limitations: > 1. Script tags inside templates are not evaluated. - /// > 2. Page styles are not visible inside templates. + /// and markup have the following + /// limitations: > 1. Script tags inside templates are not evaluated. > 2. Page styles + /// are not visible inside templates. /// /// /// Call options @@ -1587,9 +1685,9 @@ public partial interface IPage /// /// Focuses the element, and then uses and . /// - /// can specify the intended keyboardEvent.key - /// value or a single character to generate the text for. A superset of the values can be found here. + /// can specify the intended keyboardEvent.key + /// value or a single character to generate the text for. A superset of the + /// values can be found here. /// Examples of the keys are: /// /// @@ -1605,12 +1703,12 @@ public partial interface IPage /// resolves to Control on Windows and Linux and to Meta on macOS. /// /// - /// Holding down Shift will type the text that corresponds to the + /// Holding down Shift will type the text that corresponds to the /// in the upper case. /// /// - /// If is a single character, it is case-sensitive, so the values - /// a and A will generate different respective texts. + /// If is a single character, it is case-sensitive, so + /// the values a and A will generate different respective texts. /// /// /// Shortcuts such as key: "Control+o", key: "Control++ or key: "Control+Shift+T" @@ -1692,8 +1790,7 @@ public partial interface IPage /// /// /// After executing the handler, Playwright will ensure that overlay that triggered - /// the handler is not visible anymore. You can opt-out of this behavior with . + /// the handler is not visible anymore. You can opt-out of this behavior with . /// /// /// The execution time of the handler counts towards the timeout of the action/assertion @@ -1704,6 +1801,26 @@ public partial interface IPage /// at a time. Make sure the actions within a handler don't depend on another handler. /// /// + /// + /// Running the handler will alter your page state mid-test. For example it will change + /// the currently focused element and move the mouse. Make sure that actions that run + /// after the handler are self-contained and do not rely on the focus and mouse state + /// being unchanged. + /// + /// + /// For example, consider a test that calls followed + /// by . If your handler clicks a button between these + /// two actions, the focused element most likely will be wrong, and key press will happen + /// on the unexpected element. Use instead to avoid + /// this problem. + /// + /// + /// Another example is a series of mouse actions, where + /// is followed by . Again, when the handler runs between + /// these two actions, the mouse position will be wrong during the mouse down. Prefer + /// self-contained actions like that do not rely on + /// the state being unchanged by a handler. + /// /// **Usage** /// An example that closes a "Sign up to the newsletter" dialog when it appears: /// @@ -1730,8 +1847,8 @@ public partial interface IPage /// /// An example with a custom callback on every actionability check. It uses a <body> /// locator that is always visible, so the handler is called before every actionability - /// check. It is important to specify , because the handler - /// does not hide the <body> element. + /// check. It is important to specify , because + /// the handler does not hide the <body> element. /// /// /// // Setup the handler.
@@ -1745,7 +1862,7 @@ public partial interface IPage ///
/// /// Handler takes the original locator as an argument. You can also automatically remove - /// the handler after a number of invocations by setting : + /// the handler after a number of invocations by setting : /// /// /// await page.AddLocatorHandlerAsync(page.GetByText("Sign up to the newsletter"), async locator => {
@@ -1758,21 +1875,21 @@ public partial interface IPage /// Running the handler will alter your page state mid-test. For example it will change /// the currently focused element and move the mouse. Make sure that actions that run /// after the handler are self-contained and do not rely on the focus and mouse state - /// being unchanged.

For example, consider a test that calls + /// being unchanged. For example, consider a test that calls /// followed by . If your handler clicks a button /// between these two actions, the focused element most likely will be wrong, and key /// press will happen on the unexpected element. Use - /// instead to avoid this problem.

Another example is a series of mouse - /// actions, where is followed by . - /// Again, when the handler runs between these two actions, the mouse position will - /// be wrong during the mouse down. Prefer self-contained actions like + /// instead to avoid this problem. Another example is a series of mouse actions, where + /// is followed by . Again, + /// when the handler runs between these two actions, the mouse position will be wrong + /// during the mouse down. Prefer self-contained actions like /// that do not rely on the state being unchanged by a handler. ///
///
/// Locator that triggers the handler. /// - /// Function that should be run once appears. This function - /// should get rid of the element that blocks actions like click. + /// Function that should be run once appears. + /// This function should get rid of the element that blocks actions like click. /// /// Call options Task AddLocatorHandlerAsync(ILocator locator, Func handler, PageAddLocatorHandlerOptions? options = default); @@ -1811,6 +1928,17 @@ public partial interface IPage /// Once routing is enabled, every request matching the url pattern will stall unless /// it's continued, fulfilled or aborted. /// + /// The handler will only be called for the first url if the response is a redirect. + /// + /// will not intercept requests intercepted by Service + /// Worker. See this + /// issue. We recommend disabling Service Workers when using request interception by + /// setting to 'block'. + /// + /// + /// will not intercept the first request of a popup page. + /// Use instead. + /// /// **Usage** /// An example of a naive handler that aborts all image requests: /// @@ -1842,14 +1970,18 @@ public partial interface IPage /// when request matches both handlers. /// /// To remove a route with its handler you can use . + /// Enabling routing disables http cache. /// /// - /// The handler will only be called for the first url if the response is a redirect. + /// + /// The handler will only be called for the first url if the response is a redirect. + /// + /// /// /// will not intercept requests intercepted by Service /// Worker. See this /// issue. We recommend disabling Service Workers when using request interception by - /// setting to 'block'. + /// setting to 'block'. /// /// /// will not intercept the first request of a popup page. @@ -1859,8 +1991,8 @@ public partial interface IPage /// /// /// A glob pattern, regex pattern or predicate receiving to match - /// while routing. When a via the context options was provided - /// and the passed URL is a path, it gets merged via the new + /// while routing. When a via the context options + /// was provided and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// handler function to route the request. @@ -1873,6 +2005,17 @@ public partial interface IPage /// Once routing is enabled, every request matching the url pattern will stall unless /// it's continued, fulfilled or aborted. /// + /// The handler will only be called for the first url if the response is a redirect. + /// + /// will not intercept requests intercepted by Service + /// Worker. See this + /// issue. We recommend disabling Service Workers when using request interception by + /// setting to 'block'. + /// + /// + /// will not intercept the first request of a popup page. + /// Use instead. + /// /// **Usage** /// An example of a naive handler that aborts all image requests: /// @@ -1904,14 +2047,18 @@ public partial interface IPage /// when request matches both handlers. /// /// To remove a route with its handler you can use . + /// Enabling routing disables http cache. /// /// - /// The handler will only be called for the first url if the response is a redirect. + /// + /// The handler will only be called for the first url if the response is a redirect. + /// + /// /// /// will not intercept requests intercepted by Service /// Worker. See this /// issue. We recommend disabling Service Workers when using request interception by - /// setting to 'block'. + /// setting to 'block'. /// /// /// will not intercept the first request of a popup page. @@ -1921,8 +2068,8 @@ public partial interface IPage /// /// /// A glob pattern, regex pattern or predicate receiving to match - /// while routing. When a via the context options was provided - /// and the passed URL is a path, it gets merged via the new + /// while routing. When a via the context options + /// was provided and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// handler function to route the request. @@ -1935,6 +2082,17 @@ public partial interface IPage /// Once routing is enabled, every request matching the url pattern will stall unless /// it's continued, fulfilled or aborted. /// + /// The handler will only be called for the first url if the response is a redirect. + /// + /// will not intercept requests intercepted by Service + /// Worker. See this + /// issue. We recommend disabling Service Workers when using request interception by + /// setting to 'block'. + /// + /// + /// will not intercept the first request of a popup page. + /// Use instead. + /// /// **Usage** /// An example of a naive handler that aborts all image requests: /// @@ -1966,14 +2124,18 @@ public partial interface IPage /// when request matches both handlers. /// /// To remove a route with its handler you can use . + /// Enabling routing disables http cache. /// /// - /// The handler will only be called for the first url if the response is a redirect. + /// + /// The handler will only be called for the first url if the response is a redirect. + /// + /// /// /// will not intercept requests intercepted by Service /// Worker. See this /// issue. We recommend disabling Service Workers when using request interception by - /// setting to 'block'. + /// setting to 'block'. /// /// /// will not intercept the first request of a popup page. @@ -1983,8 +2145,8 @@ public partial interface IPage /// /// /// A glob pattern, regex pattern or predicate receiving to match - /// while routing. When a via the context options was provided - /// and the passed URL is a path, it gets merged via the new + /// while routing. When a via the context options + /// was provided and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// handler function to route the request. @@ -2001,7 +2163,7 @@ public partial interface IPage /// Playwright will not serve requests intercepted by Service Worker from the HAR file. /// See this issue. /// We recommend disabling Service Workers when using request interception by setting - /// to 'block'. + /// to 'block'. /// /// /// @@ -2012,6 +2174,87 @@ public partial interface IPage /// Call options Task RouteFromHARAsync(string har, PageRouteFromHAROptions? options = default); + /// + /// This method allows to modify websocket connections that are made by the page. + /// + /// Note that only WebSockets created after this method was called will be routed. + /// It is recommended to call this method before navigating the page. + /// + /// **Usage** + /// + /// Below is an example of a simple mock that responds to a single message. See for more details and examples. + /// + /// + /// await page.RouteWebSocketAsync("/ws", ws => {
+ /// ws.OnMessage(message => {
+ /// if (message == "request")
+ /// ws.Send("response");
+ /// });
+ /// }); + ///
+ ///
+ /// + /// Only WebSockets with the url matching this pattern will be routed. A string pattern + /// can be relative to the context option. + /// + /// Handler function to route the WebSocket. + Task RouteWebSocketAsync(string url, Action handler); + + /// + /// This method allows to modify websocket connections that are made by the page. + /// + /// Note that only WebSockets created after this method was called will be routed. + /// It is recommended to call this method before navigating the page. + /// + /// **Usage** + /// + /// Below is an example of a simple mock that responds to a single message. See for more details and examples. + /// + /// + /// await page.RouteWebSocketAsync("/ws", ws => {
+ /// ws.OnMessage(message => {
+ /// if (message == "request")
+ /// ws.Send("response");
+ /// });
+ /// }); + ///
+ ///
+ /// + /// Only WebSockets with the url matching this pattern will be routed. A string pattern + /// can be relative to the context option. + /// + /// Handler function to route the WebSocket. + Task RouteWebSocketAsync(Regex url, Action handler); + + /// + /// This method allows to modify websocket connections that are made by the page. + /// + /// Note that only WebSockets created after this method was called will be routed. + /// It is recommended to call this method before navigating the page. + /// + /// **Usage** + /// + /// Below is an example of a simple mock that responds to a single message. See for more details and examples. + /// + /// + /// await page.RouteWebSocketAsync("/ws", ws => {
+ /// ws.OnMessage(message => {
+ /// if (message == "request")
+ /// ws.Send("response");
+ /// });
+ /// }); + ///
+ ///
+ /// + /// Only WebSockets with the url matching this pattern will be routed. A string pattern + /// can be relative to the context option. + /// + /// Handler function to route the WebSocket. + Task RouteWebSocketAsync(Func url, Action handler); + /// Returns the buffer with the captured screenshot. /// Call options Task ScreenshotAsync(PageScreenshotOptions? options = default); @@ -2022,10 +2265,10 @@ public partial interface IPage /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -2067,10 +2310,10 @@ public partial interface IPage /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -2112,10 +2355,10 @@ public partial interface IPage /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -2157,10 +2400,10 @@ public partial interface IPage /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -2202,10 +2445,10 @@ public partial interface IPage /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -2247,10 +2490,10 @@ public partial interface IPage /// locators. /// /// - /// This method waits for an element matching , waits for - /// actionability checks, - /// waits until all specified options are present in the <select> element - /// and selects these options. + /// This method waits for an element matching , + /// waits for actionability + /// checks, waits until all specified options are present in the <select> + /// element and selects these options. /// /// /// If the target element is not a <select> element, this method throws @@ -2292,13 +2535,13 @@ public partial interface IPage /// locators. /// /// - /// This method checks or unchecks an element matching by - /// performing the following steps: + /// This method checks or unchecks an element matching + /// by performing the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, + /// wait until a matching element is attached to the DOM. /// /// /// Ensure that matched element is a checkbox or a radio input. If not, this method @@ -2307,15 +2550,15 @@ public partial interface IPage /// If the element already has the right checked state, this method returns immediately. /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option + /// is set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. /// Ensure that the element is now checked or unchecked. If not, this method throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -2352,11 +2595,16 @@ public partial interface IPage /// /// /// + /// + /// takes priority over , + /// and . + /// /// /// /// /// takes priority over , /// and . + /// /// /// /// Maximum navigation time in milliseconds @@ -2365,14 +2613,26 @@ public partial interface IPage /// /// /// This setting will change the default maximum time for all the methods accepting - /// option. + /// option. /// + /// takes priority over . /// - /// takes priority over . + /// + /// + /// takes priority over . + /// + /// + /// /// Maximum time in milliseconds void SetDefaultTimeout(float timeout); - /// The extra HTTP headers will be sent with every request the page initiates. + /// + /// The extra HTTP headers will be sent with every request the page initiates. + /// + /// does not guarantee the order of headers + /// in the outgoing requests. + /// + /// /// /// /// does not guarantee the order of headers @@ -2397,7 +2657,7 @@ public partial interface IPage /// attribute, only a single directory path is supported. /// /// - /// This method expects to point to an input + /// This method expects to point to an input /// element. However, if the element is inside the <label> element /// that has an associated control, /// targets the control instead. @@ -2424,7 +2684,7 @@ public partial interface IPage /// attribute, only a single directory path is supported. /// /// - /// This method expects to point to an input + /// This method expects to point to an input /// element. However, if the element is inside the <label> element /// that has an associated control, /// targets the control instead. @@ -2451,7 +2711,7 @@ public partial interface IPage /// attribute, only a single directory path is supported. /// /// - /// This method expects to point to an input + /// This method expects to point to an input /// element. However, if the element is inside the <label> element /// that has an associated control, /// targets the control instead. @@ -2478,7 +2738,7 @@ public partial interface IPage /// attribute, only a single directory path is supported. /// /// - /// This method expects to point to an input + /// This method expects to point to an input /// element. However, if the element is inside the <label> element /// that has an associated control, /// targets the control instead. @@ -2513,43 +2773,45 @@ public partial interface IPage /// await page.GotoAsync("https://www.microsoft.com"); ///
/// - /// - /// - /// - /// + /// Page width in pixels. + /// Page height in pixels. Task SetViewportSizeAsync(int width, int height); /// /// Use locator-based instead. Read more about locators. /// - /// This method taps an element matching by performing the - /// following steps: + /// This method taps an element matching by performing + /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until + /// Find an element matching . If there is none, wait until /// a matching element is attached to the DOM. /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is set. + /// If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// /// Use to tap the center of the element, or the specified - /// . + /// . /// /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// + /// + /// the method will throw if + /// option of the browser context is false. + /// /// /// /// - /// the method will throw if + /// the method will throw if /// option of the browser context is false. /// /// @@ -2608,13 +2870,13 @@ public partial interface IPage /// href="https://playwright.dev/dotnet/docs/locators">locators. /// /// - /// This method unchecks an element matching by performing + /// This method unchecks an element matching by performing /// the following steps: /// /// /// - /// Find an element matching . If there is none, wait until - /// a matching element is attached to the DOM. + /// Find an element matching . If there is none, wait + /// until a matching element is attached to the DOM. /// /// /// Ensure that matched element is a checkbox or a radio input. If not, this method @@ -2622,15 +2884,15 @@ public partial interface IPage /// /// /// Wait for actionability - /// checks on the matched element, unless option is set. If - /// the element is detached during the checks, the whole action is retried. + /// checks on the matched element, unless option is + /// set. If the element is detached during the checks, the whole action is retried. /// /// Scroll the element into view if needed. /// Use to click in the center of the element. /// Ensure that the element is now unchecked. If not, this method throws. /// /// - /// When all steps combined have not finished during the specified , + /// When all steps combined have not finished during the specified , /// this method throws a . Passing zero timeout disables /// this. /// @@ -2648,8 +2910,8 @@ public partial interface IPage /// /// - /// Removes a route created with . When - /// is not specified, removes all routes for the . + /// Removes a route created with . When + /// is not specified, removes all routes for the . /// /// /// @@ -2661,8 +2923,8 @@ public partial interface IPage /// /// - /// Removes a route created with . When - /// is not specified, removes all routes for the . + /// Removes a route created with . When + /// is not specified, removes all routes for the . /// /// /// @@ -2674,8 +2936,8 @@ public partial interface IPage /// /// - /// Removes a route created with . When - /// is not specified, removes all routes for the . + /// Removes a route created with . When + /// is not specified, removes all routes for the . /// /// /// @@ -2765,8 +3027,8 @@ public partial interface IPage /// /// - /// Returns when the returns a truthy value. It resolves - /// to a JSHandle of the truthy value. + /// Returns when the returns a truthy value. + /// It resolves to a JSHandle of the truthy value. /// /// **Usage** /// @@ -2802,7 +3064,7 @@ public partial interface IPage /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . /// Call options Task WaitForFunctionAsync(string expression, object? arg = default, PageWaitForFunctionOptions? options = default); @@ -2813,6 +3075,10 @@ public partial interface IPage /// The navigation must have been committed when this method is called. If current document /// has already reached the required state, resolves immediately. /// + /// + /// Most of the time, this method is not needed because Playwright auto-waits + /// before every action. + /// /// **Usage** /// /// await page.GetByRole(AriaRole.Button).ClickAsync(); // Click triggers navigation.
@@ -2878,6 +3144,10 @@ public partial interface IPage ///
/// // The method continues after navigation has finished ///
+ /// + /// Usage of the History + /// API to change the URL is considered a navigation. + /// ///
/// /// @@ -2916,6 +3186,10 @@ public partial interface IPage ///
/// // The method continues after navigation has finished ///
+ /// + /// Usage of the History + /// API to change the URL is considered a navigation. + /// /// /// /// @@ -2973,8 +3247,8 @@ public partial interface IPage /// /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3002,8 +3276,8 @@ public partial interface IPage /// /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3031,8 +3305,8 @@ public partial interface IPage /// /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3061,8 +3335,8 @@ public partial interface IPage /// Action that triggers the event. /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3091,8 +3365,8 @@ public partial interface IPage /// Action that triggers the event. /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3121,8 +3395,8 @@ public partial interface IPage /// Action that triggers the event. /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3175,8 +3449,8 @@ public partial interface IPage /// /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3204,8 +3478,8 @@ public partial interface IPage /// /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3233,8 +3507,8 @@ public partial interface IPage /// /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3263,8 +3537,8 @@ public partial interface IPage /// Action that triggers the event. /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3293,8 +3567,8 @@ public partial interface IPage /// Action that triggers the event. /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3323,8 +3597,8 @@ public partial interface IPage /// Action that triggers the event. /// /// Request URL string, regex or predicate receiving object. - /// When a via the context options was provided and the passed - /// URL is a path, it gets merged via the new + /// When a via the context options was provided + /// and the passed URL is a path, it gets merged via the new /// URL() constructor. /// /// Call options @@ -3336,15 +3610,20 @@ public partial interface IPage /// instead. Read more about locators. /// /// - /// Returns when element specified by selector satisfies option. - /// Returns null if waiting for hidden or detached. + /// Returns when element specified by selector satisfies + /// option. Returns null if waiting for hidden or detached. + /// + /// + /// Playwright automatically waits for element to be ready before performing an action. + /// Using objects and web-first assertions makes the code wait-for-selector-free. /// /// - /// Wait for the to satisfy option - /// (either appear/disappear from dom, or become visible/hidden). If at the moment of - /// calling the method already satisfies the condition, - /// the method will return immediately. If the selector doesn't satisfy the condition - /// for the milliseconds, the function will throw. + /// Wait for the to satisfy + /// option (either appear/disappear from dom, or become visible/hidden). If at the moment + /// of calling the method already satisfies + /// the condition, the method will return immediately. If the selector doesn't satisfy + /// the condition for the milliseconds, the + /// function will throw. /// /// **Usage** /// This method works across navigations: @@ -3377,6 +3656,7 @@ public partial interface IPage /// /// Playwright automatically waits for element to be ready before performing an action. /// Using objects and web-first assertions makes the code wait-for-selector-free. + /// /// /// /// A selector to query for. @@ -3388,7 +3668,7 @@ public partial interface IPage /// Never wait for timeout in production. Tests that wait for time are inherently flaky. /// Use actions and web assertions that wait automatically. /// - /// Waits for the given in milliseconds. + /// Waits for the given in milliseconds. /// /// Note that page.waitForTimeout() should only be used for debugging. Tests /// using the timer in production are going to be flaky. Use signals such as network @@ -3505,6 +3785,7 @@ public partial interface IPage /// This method returns all of the dedicated WebWorkers /// associated with the page. /// + /// This does not contain ServiceWorkers /// /// This does not contain ServiceWorkers IReadOnlyList Workers { get; } diff --git a/src/Playwright/API/Generated/IPageAssertions.cs b/src/Playwright/API/Generated/IPageAssertions.cs index 5bc9bf3794..c5b13fb728 100644 --- a/src/Playwright/API/Generated/IPageAssertions.cs +++ b/src/Playwright/API/Generated/IPageAssertions.cs @@ -60,14 +60,14 @@ public partial interface IPageAssertions /// Makes the assertion check for the opposite condition. For example, this code tests /// that the page URL doesn't contain "error": /// - /// await Expect(Page).Not.ToHaveURL("error"); + /// await Expect(Page).Not.ToHaveURLAsync("error"); /// public IPageAssertions Not { get; } /// /// Ensures the page has the given title. /// **Usage** - /// await Expect(Page).ToHaveTitle("Playwright"); + /// await Expect(Page).ToHaveTitleAsync("Playwright"); /// /// Expected title or RegExp. /// Call options @@ -76,7 +76,7 @@ public partial interface IPageAssertions /// /// Ensures the page has the given title. /// **Usage** - /// await Expect(Page).ToHaveTitle("Playwright"); + /// await Expect(Page).ToHaveTitleAsync("Playwright"); /// /// Expected title or RegExp. /// Call options @@ -85,7 +85,7 @@ public partial interface IPageAssertions /// /// Ensures the page is navigated to the given URL. /// **Usage** - /// await Expect(Page).ToHaveURL(new Regex(".*checkout")); + /// await Expect(Page).ToHaveURLAsync(new Regex(".*checkout")); /// /// Expected URL string or RegExp. /// Call options @@ -94,7 +94,7 @@ public partial interface IPageAssertions /// /// Ensures the page is navigated to the given URL. /// **Usage** - /// await Expect(Page).ToHaveURL(new Regex(".*checkout")); + /// await Expect(Page).ToHaveURLAsync(new Regex(".*checkout")); /// /// Expected URL string or RegExp. /// Call options diff --git a/src/Playwright/API/Generated/IRequest.cs b/src/Playwright/API/Generated/IRequest.cs index 7196c292f7..881e557bd4 100644 --- a/src/Playwright/API/Generated/IRequest.cs +++ b/src/Playwright/API/Generated/IRequest.cs @@ -52,6 +52,10 @@ namespace Microsoft.Playwright; /// is emitted. /// /// +/// HTTP Error responses, such as 404 or 503, are still successful responses from HTTP +/// standpoint, so request will complete with 'requestfinished' event. +/// +/// /// If request gets a 'redirect' response, the request is successfully finished with /// the requestfinished event, and a new request is issued to a redirected url. /// diff --git a/src/Playwright/API/Generated/IRoute.cs b/src/Playwright/API/Generated/IRoute.cs index 55fddac265..61fe1859c3 100644 --- a/src/Playwright/API/Generated/IRoute.cs +++ b/src/Playwright/API/Generated/IRoute.cs @@ -85,7 +85,7 @@ public partial interface IRoute /// /// **Details** /// - /// Note that any overrides such as or + /// Note that any overrides such as or /// only apply to the request being routed. If this request results in a redirect, overrides /// will not be applied to the new redirected request. If you want to propagate a header /// through redirects, use the combination of and /// **Details** /// - /// Note that option will apply to the fetched request as - /// well as any redirects initiated by it. If you want to only apply + /// Note that option will apply to the fetched request + /// as well as any redirects initiated by it. If you want to only apply /// to the original request, but not to redirects, look into /// instead. /// diff --git a/src/Playwright/API/Generated/ITouchscreen.cs b/src/Playwright/API/Generated/ITouchscreen.cs index 00ff962b58..519b02eb37 100644 --- a/src/Playwright/API/Generated/ITouchscreen.cs +++ b/src/Playwright/API/Generated/ITouchscreen.cs @@ -40,12 +40,16 @@ public partial interface ITouchscreen /// /// /// Dispatches a touchstart and touchend event with a single touch at - /// the position (,). + /// the position (,). + /// + /// + /// the method will throw if + /// option of the browser context is false. /// /// /// /// - /// the method will throw if + /// the method will throw if /// option of the browser context is false. /// /// diff --git a/src/Playwright/API/Generated/IWebSocketRoute.cs b/src/Playwright/API/Generated/IWebSocketRoute.cs new file mode 100644 index 0000000000..66c01b52e2 --- /dev/null +++ b/src/Playwright/API/Generated/IWebSocketRoute.cs @@ -0,0 +1,195 @@ +/* + * MIT License + * + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System; +using System.Threading.Tasks; + +#nullable enable + +namespace Microsoft.Playwright; + +/// +/// +/// Whenever a WebSocket +/// route is set up with or , +/// the WebSocketRoute object allows to handle the WebSocket, like an actual +/// server would do. +/// +/// **Mocking** +/// +/// By default, the routed WebSocket will not connect to the server. This way, you can +/// mock entire communcation over the WebSocket. Here is an example that responds to +/// a "request" with a "response". +/// +/// +/// await page.RouteWebSocketAsync("/ws", ws => {
+/// ws.OnMessage(message => {
+/// if (message == "request")
+/// ws.Send("response");
+/// });
+/// }); +///
+/// +/// Since we do not call inside the WebSocket +/// route handler, Playwright assumes that WebSocket will be mocked, and opens the WebSocket +/// inside the page automatically. +/// +/// **Intercepting** +/// +/// Alternatively, you may want to connect to the actual server, but intercept messages +/// in-between and modify or block them. Calling +/// returns a server-side WebSocketRoute instance that you can send messages +/// to, or handle incoming messages. +/// +/// +/// Below is an example that modifies some messages sent by the page to the server. +/// Messages sent from the server to the page are left intact, relying on the default +/// forwarding. +/// +/// +/// await page.RouteWebSocketAsync("/ws", ws => {
+/// var server = ws.ConnectToServer();
+/// ws.OnMessage(message => {
+/// if (message == "request")
+/// server.Send("request2");
+/// else
+/// server.Send(message);
+/// });
+/// }); +///
+/// +/// After connecting to the server, all **messages are forwarded** between the page +/// and the server by default. +/// +/// +/// However, if you call on the original route, +/// messages from the page to the server **will not be forwarded** anymore, but should +/// instead be handled by the . +/// +/// +/// Similarly, calling on the server-side WebSocket +/// will **stop forwarding messages** from the server to the page, and +/// should take care of them. +/// +/// +/// The following example blocks some messages in both directions. Since it calls in both directions, there is no automatic forwarding +/// at all. +/// +/// +/// await page.RouteWebSocketAsync("/ws", ws => {
+/// var server = ws.ConnectToServer();
+/// ws.OnMessage(message => {
+/// if (message != "blocked-from-the-page")
+/// server.Send(message);
+/// });
+/// server.OnMessage(message => {
+/// if (message != "blocked-from-the-server")
+/// ws.Send(message);
+/// });
+/// }); +///
+///
+public partial interface IWebSocketRoute +{ + /// Closes one side of the WebSocket connection. + /// Call options + Task CloseAsync(WebSocketRouteCloseOptions? options = default); + + /// + /// + /// By default, routed WebSocket does not connect to the server, so you can mock entire + /// WebSocket communication. This method connects to the actual WebSocket server, and + /// returns the server-side instance, giving the ability + /// to send and receive messages from the server. + /// + /// Once connected to the server: + /// + /// + /// Messages received from the server will be **automatically forwarded** to the WebSocket + /// in the page, unless is called on the server-side + /// WebSocketRoute. + /// + /// + /// Messages sent by the WebSocket.send() + /// call in the page will be **automatically forwarded** to the server, unless is called on the original WebSocketRoute. + /// + /// + /// See examples at the top for more details. + /// + IWebSocketRoute ConnectToServer(); + + /// + /// Allows to handle WebSocket.close. + /// + /// By default, closing one side of the connection, either in the page or on the server, + /// will close the other side. However, when handler + /// is set up, the default forwarding of closure is disabled, and handler should take + /// care of it. + /// + /// + /// + /// Function that will handle WebSocket closure. Received an optional close + /// code and an optional close + /// reason. + /// + void OnClose(Action handler); + + /// + /// + /// This method allows to handle messages that are sent by the WebSocket, either from + /// the page or from the server. + /// + /// + /// When called on the original WebSocket route, this method handles messages sent from + /// the page. You can handle this messages by responding to them with , + /// forwarding them to the server-side connection returned by + /// or do something else. + /// + /// + /// Once this method is called, messages are not automatically forwarded to the server + /// or to the page - you should do that manually by calling . + /// See examples at the top for more details. + /// + /// Calling this method again will override the handler with a new one. + /// + /// Function that will handle messages. + void OnMessage(Action handler); + + /// + /// + /// Sends a message to the WebSocket. When called on the original WebSocket, sends the + /// message to the page. When called on the result of , + /// sends the message to the server. See examples at the top for more details. + /// + /// + /// Message to send. + void Send(byte[] message); + + /// URL of the WebSocket created in the page. + string Url { get; } +} + +#nullable disable diff --git a/src/Playwright/API/Generated/IWorker.cs b/src/Playwright/API/Generated/IWorker.cs index 482ac24000..a3a31e0b16 100644 --- a/src/Playwright/API/Generated/IWorker.cs +++ b/src/Playwright/API/Generated/IWorker.cs @@ -60,7 +60,7 @@ public partial interface IWorker event EventHandler Close; /// - /// Returns the return value of . + /// Returns the return value of . /// /// If the function passed to the returns a , then would wait for the promise @@ -77,11 +77,14 @@ public partial interface IWorker /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvaluateAsync(string expression, object? arg = default); /// - /// Returns the return value of as a . + /// + /// Returns the return value of as a . + /// /// /// The only difference between and /// is that returns . @@ -96,7 +99,7 @@ public partial interface IWorker /// JavaScript expression to be evaluated in the browser context. If the expression /// evaluates to a function, the function is automatically invoked. /// - /// Optional argument to pass to . + /// Optional argument to pass to . Task EvaluateHandleAsync(string expression, object? arg = default); string Url { get; } diff --git a/src/Playwright/API/Generated/Options/APIRequestContextStorageStateOptions.cs b/src/Playwright/API/Generated/Options/APIRequestContextStorageStateOptions.cs index 26cf71950c..fe4bb5e290 100644 --- a/src/Playwright/API/Generated/Options/APIRequestContextStorageStateOptions.cs +++ b/src/Playwright/API/Generated/Options/APIRequestContextStorageStateOptions.cs @@ -44,9 +44,10 @@ public APIRequestContextStorageStateOptions(APIRequestContextStorageStateOptions /// /// - /// The file path to save the storage state to. If is a relative - /// path, then it is resolved relative to current working directory. If no path is provided, - /// storage state is still returned, but won't be saved to the disk. + /// The file path to save the storage state to. If + /// is a relative path, then it is resolved relative to current working directory. If + /// no path is provided, storage state is still returned, but won't be saved to the + /// disk. /// /// [JsonPropertyName("path")] diff --git a/src/Playwright/API/Generated/Options/APIRequestNewContextOptions.cs b/src/Playwright/API/Generated/Options/APIRequestNewContextOptions.cs index d70da7d624..01f32b1d12 100644 --- a/src/Playwright/API/Generated/Options/APIRequestNewContextOptions.cs +++ b/src/Playwright/API/Generated/Options/APIRequestNewContextOptions.cs @@ -90,12 +90,16 @@ public APIRequestNewContextOptions(APIRequestNewContextOptions clone) /// origin property should be provided with an exact match to the request origin /// that the certificate is valid for. /// + /// + /// When using WebKit on macOS, accessing localhost will not pick up client certificates. + /// You can make it work by replacing localhost with local.playwright. + /// /// /// - /// Using Client Certificates in combination with Proxy Servers is not supported. /// /// When using WebKit on macOS, accessing localhost will not pick up client certificates. /// You can make it work by replacing localhost with local.playwright. + /// /// /// [JsonPropertyName("clientCertificates")] diff --git a/src/Playwright/API/Generated/Options/BrowserContextStorageStateOptions.cs b/src/Playwright/API/Generated/Options/BrowserContextStorageStateOptions.cs index 35ff8da248..1b48d74bad 100644 --- a/src/Playwright/API/Generated/Options/BrowserContextStorageStateOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserContextStorageStateOptions.cs @@ -44,9 +44,10 @@ public BrowserContextStorageStateOptions(BrowserContextStorageStateOptions clone /// /// - /// The file path to save the storage state to. If is a relative - /// path, then it is resolved relative to current working directory. If no path is provided, - /// storage state is still returned, but won't be saved to the disk. + /// The file path to save the storage state to. If + /// is a relative path, then it is resolved relative to current working directory. If + /// no path is provided, storage state is still returned, but won't be saved to the + /// disk. /// /// [JsonPropertyName("path")] diff --git a/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs b/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs index 321698416a..16115891ff 100644 --- a/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs @@ -131,12 +131,16 @@ public BrowserNewContextOptions(BrowserNewContextOptions clone) /// origin property should be provided with an exact match to the request origin /// that the certificate is valid for. /// + /// + /// When using WebKit on macOS, accessing localhost will not pick up client certificates. + /// You can make it work by replacing localhost with local.playwright. + /// /// /// - /// Using Client Certificates in combination with Proxy Servers is not supported. /// /// When using WebKit on macOS, accessing localhost will not pick up client certificates. /// You can make it work by replacing localhost with local.playwright. + /// /// /// [JsonPropertyName("clientCertificates")] @@ -347,7 +351,7 @@ public BrowserNewContextOptions(BrowserNewContextOptions clone) /// /// /// Emulates consistent window screen size available inside web page via window.screen. - /// Is only used when the is set. + /// Is only used when the is set. /// /// [JsonPropertyName("screen")] @@ -417,6 +421,11 @@ public BrowserNewContextOptions(BrowserNewContextOptions clone) /// more about viewport /// emulation. ///
+ /// + /// The ViewportSize.NoViewport value opts out from the default presets, makes + /// viewport depend on the host window size defined by the operating system. It makes + /// the execution of the tests non-deterministic. + /// /// /// /// diff --git a/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs b/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs index 73a6fd1d49..ec899f6ac7 100644 --- a/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs @@ -131,12 +131,16 @@ public BrowserNewPageOptions(BrowserNewPageOptions clone) /// origin property should be provided with an exact match to the request origin /// that the certificate is valid for. /// + /// + /// When using WebKit on macOS, accessing localhost will not pick up client certificates. + /// You can make it work by replacing localhost with local.playwright. + /// /// /// - /// Using Client Certificates in combination with Proxy Servers is not supported. /// /// When using WebKit on macOS, accessing localhost will not pick up client certificates. /// You can make it work by replacing localhost with local.playwright. + /// /// /// [JsonPropertyName("clientCertificates")] @@ -347,7 +351,7 @@ public BrowserNewPageOptions(BrowserNewPageOptions clone) /// /// /// Emulates consistent window screen size available inside web page via window.screen. - /// Is only used when the is set. + /// Is only used when the is set. /// /// [JsonPropertyName("screen")] @@ -417,6 +421,11 @@ public BrowserNewPageOptions(BrowserNewPageOptions clone) /// more about viewport /// emulation. /// + /// + /// The ViewportSize.NoViewport value opts out from the default presets, makes + /// viewport depend on the host window size defined by the operating system. It makes + /// the execution of the tests non-deterministic. + /// /// /// /// diff --git a/src/Playwright/API/Generated/Options/BrowserTypeLaunchOptions.cs b/src/Playwright/API/Generated/Options/BrowserTypeLaunchOptions.cs index 69bd4f06f9..867662c076 100644 --- a/src/Playwright/API/Generated/Options/BrowserTypeLaunchOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserTypeLaunchOptions.cs @@ -61,12 +61,18 @@ public BrowserTypeLaunchOptions(BrowserTypeLaunchOptions clone) } /// + /// Use custom browser args at your own risk, as some of them may break Playwright functionality. /// /// Additional arguments to pass to the browser instance. The list of Chromium flags /// can be found here. /// /// - /// Use custom browser args at your own risk, as some of them may break Playwright functionality. + /// + /// + /// Use custom browser args at your own risk, as some of them may break Playwright functionality. + /// + /// + /// [JsonPropertyName("args")] public IEnumerable? Args { get; set; } @@ -92,7 +98,8 @@ public BrowserTypeLaunchOptions(BrowserTypeLaunchOptions clone) /// /// /// **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If - /// this option is true, the option will be set false. + /// this option is true, the option will + /// be set false. /// /// [JsonPropertyName("devtools")] @@ -116,7 +123,7 @@ public BrowserTypeLaunchOptions(BrowserTypeLaunchOptions clone) /// /// - /// Path to a browser executable to run instead of the bundled one. If + /// Path to a browser executable to run instead of the bundled one. If /// is a relative path, then it is resolved relative to the current working directory. /// Note that Playwright only works with the bundled Chromium, Firefox or WebKit, use /// at your own risk. @@ -145,7 +152,8 @@ public BrowserTypeLaunchOptions(BrowserTypeLaunchOptions clone) /// /// Whether to run browser in headless mode. More details for Chromium /// and Firefox. - /// Defaults to true unless the option is true. + /// Defaults to true unless the option + /// is true. /// /// [JsonPropertyName("headless")] @@ -154,8 +162,8 @@ public BrowserTypeLaunchOptions(BrowserTypeLaunchOptions clone) /// /// /// If true, Playwright does not pass its own configurations args and only uses - /// the ones from . Dangerous option; use with care. Defaults - /// to false. + /// the ones from . Dangerous option; use with + /// care. Defaults to false. /// /// [JsonPropertyName("ignoreAllDefaultArgs")] @@ -164,7 +172,8 @@ public BrowserTypeLaunchOptions(BrowserTypeLaunchOptions clone) /// /// /// If true, Playwright does not pass its own configurations args and only uses - /// the ones from . Dangerous option; use with care. + /// the ones from . Dangerous option; use with + /// care. /// /// [JsonPropertyName("ignoreDefaultArgs")] diff --git a/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs b/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs index 3c5aaff578..a05688f6ae 100644 --- a/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs @@ -104,12 +104,18 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont public bool? AcceptDownloads { get; set; } /// + /// Use custom browser args at your own risk, as some of them may break Playwright functionality. /// /// Additional arguments to pass to the browser instance. The list of Chromium flags /// can be found here. /// /// - /// Use custom browser args at your own risk, as some of them may break Playwright functionality. + /// + /// + /// Use custom browser args at your own risk, as some of them may break Playwright functionality. + /// + /// + /// [JsonPropertyName("args")] public IEnumerable? Args { get; set; } @@ -171,12 +177,16 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont /// origin property should be provided with an exact match to the request origin /// that the certificate is valid for. /// + /// + /// When using WebKit on macOS, accessing localhost will not pick up client certificates. + /// You can make it work by replacing localhost with local.playwright. + /// /// /// - /// Using Client Certificates in combination with Proxy Servers is not supported. /// /// When using WebKit on macOS, accessing localhost will not pick up client certificates. /// You can make it work by replacing localhost with local.playwright. + /// /// /// [JsonPropertyName("clientCertificates")] @@ -210,7 +220,8 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont /// /// /// **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If - /// this option is true, the option will be set false. + /// this option is true, the + /// option will be set false. /// /// [JsonPropertyName("devtools")] @@ -234,7 +245,7 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont /// /// - /// Path to a browser executable to run instead of the bundled one. If + /// Path to a browser executable to run instead of the bundled one. If /// is a relative path, then it is resolved relative to the current working directory. /// Note that Playwright only works with the bundled Chromium, Firefox or WebKit, use /// at your own risk. @@ -294,7 +305,8 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont /// /// Whether to run browser in headless mode. More details for Chromium /// and Firefox. - /// Defaults to true unless the option is true. + /// Defaults to true unless the option + /// is true. /// /// [JsonPropertyName("headless")] @@ -313,8 +325,8 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont /// /// /// If true, Playwright does not pass its own configurations args and only uses - /// the ones from . Dangerous option; use with care. Defaults - /// to false. + /// the ones from . Dangerous + /// option; use with care. Defaults to false. /// /// [JsonPropertyName("ignoreAllDefaultArgs")] @@ -323,7 +335,8 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont /// /// /// If true, Playwright does not pass its own configurations args and only uses - /// the ones from . Dangerous option; use with care. + /// the ones from . Dangerous + /// option; use with care. /// /// [JsonPropertyName("ignoreDefaultArgs")] @@ -472,7 +485,8 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont /// /// /// Emulates consistent window screen size available inside web page via window.screen. - /// Is only used when the is set. + /// Is only used when the is + /// set. /// /// [JsonPropertyName("screen")] @@ -545,6 +559,11 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont /// more about viewport /// emulation. /// + /// + /// The ViewportSize.NoViewport value opts out from the default presets, makes + /// viewport depend on the host window size defined by the operating system. It makes + /// the execution of the tests non-deterministic. + /// /// /// /// diff --git a/src/Playwright/API/Generated/Options/ElementHandleScreenshotOptions.cs b/src/Playwright/API/Generated/Options/ElementHandleScreenshotOptions.cs index 1ecf9194d1..2793d463b3 100644 --- a/src/Playwright/API/Generated/Options/ElementHandleScreenshotOptions.cs +++ b/src/Playwright/API/Generated/Options/ElementHandleScreenshotOptions.cs @@ -85,7 +85,7 @@ public ElementHandleScreenshotOptions(ElementHandleScreenshotOptions clone) /// /// /// Specify locators that should be masked when the screenshot is taken. Masked elements - /// will be overlaid with a pink box #FF00FF (customized by ) + /// will be overlaid with a pink box #FF00FF (customized by ) /// that completely covers its bounding box. /// /// @@ -113,9 +113,9 @@ public ElementHandleScreenshotOptions(ElementHandleScreenshotOptions clone) /// /// /// The file path to save the image to. The screenshot type will be inferred from file - /// extension. If is a relative path, then it is resolved relative - /// to the current working directory. If no path is provided, the image won't be saved - /// to the disk. + /// extension. If is a relative path, then + /// it is resolved relative to the current working directory. If no path is provided, + /// the image won't be saved to the disk. /// /// [JsonPropertyName("path")] diff --git a/src/Playwright/API/Generated/Options/FrameClickOptions.cs b/src/Playwright/API/Generated/Options/FrameClickOptions.cs index 8b613b74ef..5f8983971a 100644 --- a/src/Playwright/API/Generated/Options/FrameClickOptions.cs +++ b/src/Playwright/API/Generated/Options/FrameClickOptions.cs @@ -134,7 +134,9 @@ public FrameClickOptions(FrameClickOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/FrameDblClickOptions.cs b/src/Playwright/API/Generated/Options/FrameDblClickOptions.cs index 215cb235f7..b042728593 100644 --- a/src/Playwright/API/Generated/Options/FrameDblClickOptions.cs +++ b/src/Playwright/API/Generated/Options/FrameDblClickOptions.cs @@ -124,7 +124,9 @@ public FrameDblClickOptions(FrameDblClickOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/FrameGetByRoleOptions.cs b/src/Playwright/API/Generated/Options/FrameGetByRoleOptions.cs index 1355b5a33b..89295e865b 100644 --- a/src/Playwright/API/Generated/Options/FrameGetByRoleOptions.cs +++ b/src/Playwright/API/Generated/Options/FrameGetByRoleOptions.cs @@ -63,11 +63,18 @@ public FrameGetByRoleOptions(FrameGetByRoleOptions clone) [JsonPropertyName("checked")] public bool? Checked { get; set; } - /// An attribute that is usually set by aria-disabled or disabled. + /// + /// An attribute that is usually set by aria-disabled or disabled. + /// + /// Unlike most other attributes, disabled is inherited through the DOM hierarchy. + /// Learn more about aria-disabled. + /// + /// /// /// /// Unlike most other attributes, disabled is inherited through the DOM hierarchy. /// Learn more about aria-disabled. + /// /// /// [JsonPropertyName("disabled")] @@ -75,8 +82,8 @@ public FrameGetByRoleOptions(FrameGetByRoleOptions clone) /// /// - /// Whether is matched exactly: case-sensitive and whole-string. - /// Defaults to false. Ignored when is a regular expression. + /// Whether is matched exactly: case-sensitive and whole-string. + /// Defaults to false. Ignored when is a regular expression. /// Note that exact match still trims whitespace. /// /// @@ -116,7 +123,7 @@ public FrameGetByRoleOptions(FrameGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible @@ -130,7 +137,7 @@ public FrameGetByRoleOptions(FrameGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible @@ -144,7 +151,7 @@ public FrameGetByRoleOptions(FrameGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible diff --git a/src/Playwright/API/Generated/Options/FrameHoverOptions.cs b/src/Playwright/API/Generated/Options/FrameHoverOptions.cs index ef8407e079..f293db6170 100644 --- a/src/Playwright/API/Generated/Options/FrameHoverOptions.cs +++ b/src/Playwright/API/Generated/Options/FrameHoverOptions.cs @@ -109,7 +109,9 @@ public FrameHoverOptions(FrameHoverOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/FrameLocatorGetByRoleOptions.cs b/src/Playwright/API/Generated/Options/FrameLocatorGetByRoleOptions.cs index b631e5470a..319aac2cf4 100644 --- a/src/Playwright/API/Generated/Options/FrameLocatorGetByRoleOptions.cs +++ b/src/Playwright/API/Generated/Options/FrameLocatorGetByRoleOptions.cs @@ -63,11 +63,18 @@ public FrameLocatorGetByRoleOptions(FrameLocatorGetByRoleOptions clone) [JsonPropertyName("checked")] public bool? Checked { get; set; } - /// An attribute that is usually set by aria-disabled or disabled. + /// + /// An attribute that is usually set by aria-disabled or disabled. + /// + /// Unlike most other attributes, disabled is inherited through the DOM hierarchy. + /// Learn more about aria-disabled. + /// + /// /// /// /// Unlike most other attributes, disabled is inherited through the DOM hierarchy. /// Learn more about aria-disabled. + /// /// /// [JsonPropertyName("disabled")] @@ -75,9 +82,9 @@ public FrameLocatorGetByRoleOptions(FrameLocatorGetByRoleOptions clone) /// /// - /// Whether is matched exactly: case-sensitive and whole-string. - /// Defaults to false. Ignored when is a regular expression. - /// Note that exact match still trims whitespace. + /// Whether is matched exactly: case-sensitive + /// and whole-string. Defaults to false. Ignored when + /// is a regular expression. Note that exact match still trims whitespace. /// /// [JsonPropertyName("exact")] @@ -116,7 +123,7 @@ public FrameLocatorGetByRoleOptions(FrameLocatorGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible @@ -130,7 +137,7 @@ public FrameLocatorGetByRoleOptions(FrameLocatorGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible @@ -144,7 +151,7 @@ public FrameLocatorGetByRoleOptions(FrameLocatorGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible diff --git a/src/Playwright/API/Generated/Options/FrameTapOptions.cs b/src/Playwright/API/Generated/Options/FrameTapOptions.cs index 6b17c120f9..858710326e 100644 --- a/src/Playwright/API/Generated/Options/FrameTapOptions.cs +++ b/src/Playwright/API/Generated/Options/FrameTapOptions.cs @@ -109,7 +109,9 @@ public FrameTapOptions(FrameTapOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/FrameWaitForFunctionOptions.cs b/src/Playwright/API/Generated/Options/FrameWaitForFunctionOptions.cs index cc571724c8..12c44c830a 100644 --- a/src/Playwright/API/Generated/Options/FrameWaitForFunctionOptions.cs +++ b/src/Playwright/API/Generated/Options/FrameWaitForFunctionOptions.cs @@ -46,7 +46,7 @@ public FrameWaitForFunctionOptions(FrameWaitForFunctionOptions clone) /// /// /// If specified, then it is treated as an interval in milliseconds at which the function - /// would be executed. By default if the option is not specified + /// would be executed. By default if the option is not specified /// is executed in requestAnimationFrame callback. /// /// diff --git a/src/Playwright/API/Generated/Options/LocatorAssertionsToContainTextOptions.cs b/src/Playwright/API/Generated/Options/LocatorAssertionsToContainTextOptions.cs index fc57a7c750..78d12c0968 100644 --- a/src/Playwright/API/Generated/Options/LocatorAssertionsToContainTextOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorAssertionsToContainTextOptions.cs @@ -46,8 +46,8 @@ public LocatorAssertionsToContainTextOptions(LocatorAssertionsToContainTextOptio /// /// - /// Whether to perform case-insensitive match. option - /// takes precedence over the corresponding regular expression flag if specified. + /// Whether to perform case-insensitive match. + /// option takes precedence over the corresponding regular expression flag if specified. /// /// [JsonPropertyName("ignoreCase")] diff --git a/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAccessibleDescriptionOptions.cs b/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAccessibleDescriptionOptions.cs index ae254e61ea..60d17087bd 100644 --- a/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAccessibleDescriptionOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAccessibleDescriptionOptions.cs @@ -45,8 +45,8 @@ public LocatorAssertionsToHaveAccessibleDescriptionOptions(LocatorAssertionsToHa /// /// - /// Whether to perform case-insensitive match. option - /// takes precedence over the corresponding regular expression flag if specified. + /// Whether to perform case-insensitive match. + /// option takes precedence over the corresponding regular expression flag if specified. /// /// [JsonPropertyName("ignoreCase")] diff --git a/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAccessibleNameOptions.cs b/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAccessibleNameOptions.cs index 74d1cdcb65..fcb1c86c59 100644 --- a/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAccessibleNameOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAccessibleNameOptions.cs @@ -45,8 +45,8 @@ public LocatorAssertionsToHaveAccessibleNameOptions(LocatorAssertionsToHaveAcces /// /// - /// Whether to perform case-insensitive match. option - /// takes precedence over the corresponding regular expression flag if specified. + /// Whether to perform case-insensitive match. + /// option takes precedence over the corresponding regular expression flag if specified. /// /// [JsonPropertyName("ignoreCase")] diff --git a/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAttributeOptions.cs b/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAttributeOptions.cs index 44490f511d..2ad8952ff5 100644 --- a/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAttributeOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveAttributeOptions.cs @@ -45,8 +45,8 @@ public LocatorAssertionsToHaveAttributeOptions(LocatorAssertionsToHaveAttributeO /// /// - /// Whether to perform case-insensitive match. option - /// takes precedence over the corresponding regular expression flag if specified. + /// Whether to perform case-insensitive match. + /// option takes precedence over the corresponding regular expression flag if specified. /// /// [JsonPropertyName("ignoreCase")] diff --git a/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveTextOptions.cs b/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveTextOptions.cs index de2fbd73e4..1ffc22c085 100644 --- a/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveTextOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorAssertionsToHaveTextOptions.cs @@ -46,8 +46,8 @@ public LocatorAssertionsToHaveTextOptions(LocatorAssertionsToHaveTextOptions clo /// /// - /// Whether to perform case-insensitive match. option - /// takes precedence over the corresponding regular expression flag if specified. + /// Whether to perform case-insensitive match. + /// option takes precedence over the corresponding regular expression flag if specified. /// /// [JsonPropertyName("ignoreCase")] diff --git a/src/Playwright/API/Generated/Options/LocatorClickOptions.cs b/src/Playwright/API/Generated/Options/LocatorClickOptions.cs index 325c0bf297..fdae7eabe3 100644 --- a/src/Playwright/API/Generated/Options/LocatorClickOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorClickOptions.cs @@ -124,7 +124,9 @@ public LocatorClickOptions(LocatorClickOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/LocatorDblClickOptions.cs b/src/Playwright/API/Generated/Options/LocatorDblClickOptions.cs index 674ede1bde..d1ff1157e3 100644 --- a/src/Playwright/API/Generated/Options/LocatorDblClickOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorDblClickOptions.cs @@ -114,7 +114,9 @@ public LocatorDblClickOptions(LocatorDblClickOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/LocatorGetByRoleOptions.cs b/src/Playwright/API/Generated/Options/LocatorGetByRoleOptions.cs index a4d44cb48b..74e0356d3a 100644 --- a/src/Playwright/API/Generated/Options/LocatorGetByRoleOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorGetByRoleOptions.cs @@ -63,11 +63,18 @@ public LocatorGetByRoleOptions(LocatorGetByRoleOptions clone) [JsonPropertyName("checked")] public bool? Checked { get; set; } - /// An attribute that is usually set by aria-disabled or disabled. + /// + /// An attribute that is usually set by aria-disabled or disabled. + /// + /// Unlike most other attributes, disabled is inherited through the DOM hierarchy. + /// Learn more about aria-disabled. + /// + /// /// /// /// Unlike most other attributes, disabled is inherited through the DOM hierarchy. /// Learn more about aria-disabled. + /// /// /// [JsonPropertyName("disabled")] @@ -75,9 +82,9 @@ public LocatorGetByRoleOptions(LocatorGetByRoleOptions clone) /// /// - /// Whether is matched exactly: case-sensitive and whole-string. - /// Defaults to false. Ignored when is a regular expression. - /// Note that exact match still trims whitespace. + /// Whether is matched exactly: case-sensitive and + /// whole-string. Defaults to false. Ignored when is + /// a regular expression. Note that exact match still trims whitespace. /// /// [JsonPropertyName("exact")] @@ -116,7 +123,7 @@ public LocatorGetByRoleOptions(LocatorGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible @@ -130,7 +137,7 @@ public LocatorGetByRoleOptions(LocatorGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible @@ -144,7 +151,7 @@ public LocatorGetByRoleOptions(LocatorGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible diff --git a/src/Playwright/API/Generated/Options/LocatorHoverOptions.cs b/src/Playwright/API/Generated/Options/LocatorHoverOptions.cs index cc5c4032bb..9ad0772440 100644 --- a/src/Playwright/API/Generated/Options/LocatorHoverOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorHoverOptions.cs @@ -99,7 +99,9 @@ public LocatorHoverOptions(LocatorHoverOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/LocatorScreenshotOptions.cs b/src/Playwright/API/Generated/Options/LocatorScreenshotOptions.cs index 1fca353419..1dad2bb3d5 100644 --- a/src/Playwright/API/Generated/Options/LocatorScreenshotOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorScreenshotOptions.cs @@ -85,7 +85,7 @@ public LocatorScreenshotOptions(LocatorScreenshotOptions clone) /// /// /// Specify locators that should be masked when the screenshot is taken. Masked elements - /// will be overlaid with a pink box #FF00FF (customized by ) + /// will be overlaid with a pink box #FF00FF (customized by ) /// that completely covers its bounding box. /// /// @@ -113,9 +113,9 @@ public LocatorScreenshotOptions(LocatorScreenshotOptions clone) /// /// /// The file path to save the image to. The screenshot type will be inferred from file - /// extension. If is a relative path, then it is resolved relative - /// to the current working directory. If no path is provided, the image won't be saved - /// to the disk. + /// extension. If is a relative path, then it + /// is resolved relative to the current working directory. If no path is provided, the + /// image won't be saved to the disk. /// /// [JsonPropertyName("path")] diff --git a/src/Playwright/API/Generated/Options/LocatorTapOptions.cs b/src/Playwright/API/Generated/Options/LocatorTapOptions.cs index 81160e5b67..a5951774b3 100644 --- a/src/Playwright/API/Generated/Options/LocatorTapOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorTapOptions.cs @@ -99,7 +99,9 @@ public LocatorTapOptions(LocatorTapOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/PageAssertionsToHaveURLOptions.cs b/src/Playwright/API/Generated/Options/PageAssertionsToHaveURLOptions.cs index 2bc952baa8..6585bb4884 100644 --- a/src/Playwright/API/Generated/Options/PageAssertionsToHaveURLOptions.cs +++ b/src/Playwright/API/Generated/Options/PageAssertionsToHaveURLOptions.cs @@ -45,8 +45,8 @@ public PageAssertionsToHaveURLOptions(PageAssertionsToHaveURLOptions clone) /// /// - /// Whether to perform case-insensitive match. option - /// takes precedence over the corresponding regular expression flag if specified. + /// Whether to perform case-insensitive match. + /// option takes precedence over the corresponding regular expression flag if specified. /// /// [JsonPropertyName("ignoreCase")] diff --git a/src/Playwright/API/Generated/Options/PageClickOptions.cs b/src/Playwright/API/Generated/Options/PageClickOptions.cs index ceb02edfd4..6ddd5a21eb 100644 --- a/src/Playwright/API/Generated/Options/PageClickOptions.cs +++ b/src/Playwright/API/Generated/Options/PageClickOptions.cs @@ -134,7 +134,9 @@ public PageClickOptions(PageClickOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/PageDblClickOptions.cs b/src/Playwright/API/Generated/Options/PageDblClickOptions.cs index 3d868953d5..0023f9f7bf 100644 --- a/src/Playwright/API/Generated/Options/PageDblClickOptions.cs +++ b/src/Playwright/API/Generated/Options/PageDblClickOptions.cs @@ -124,7 +124,9 @@ public PageDblClickOptions(PageDblClickOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/PageGetByRoleOptions.cs b/src/Playwright/API/Generated/Options/PageGetByRoleOptions.cs index bb246217a0..22d0333493 100644 --- a/src/Playwright/API/Generated/Options/PageGetByRoleOptions.cs +++ b/src/Playwright/API/Generated/Options/PageGetByRoleOptions.cs @@ -63,11 +63,18 @@ public PageGetByRoleOptions(PageGetByRoleOptions clone) [JsonPropertyName("checked")] public bool? Checked { get; set; } - /// An attribute that is usually set by aria-disabled or disabled. + /// + /// An attribute that is usually set by aria-disabled or disabled. + /// + /// Unlike most other attributes, disabled is inherited through the DOM hierarchy. + /// Learn more about aria-disabled. + /// + /// /// /// /// Unlike most other attributes, disabled is inherited through the DOM hierarchy. /// Learn more about aria-disabled. + /// /// /// [JsonPropertyName("disabled")] @@ -75,8 +82,8 @@ public PageGetByRoleOptions(PageGetByRoleOptions clone) /// /// - /// Whether is matched exactly: case-sensitive and whole-string. - /// Defaults to false. Ignored when is a regular expression. + /// Whether is matched exactly: case-sensitive and whole-string. + /// Defaults to false. Ignored when is a regular expression. /// Note that exact match still trims whitespace. /// /// @@ -116,7 +123,7 @@ public PageGetByRoleOptions(PageGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible @@ -130,7 +137,7 @@ public PageGetByRoleOptions(PageGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible @@ -144,7 +151,7 @@ public PageGetByRoleOptions(PageGetByRoleOptions clone) /// /// Option to match the accessible /// name. By default, matching is case-insensitive and searches for a substring, - /// use to control this behavior. + /// use to control this behavior. /// /// /// Learn more about accessible diff --git a/src/Playwright/API/Generated/Options/PageHoverOptions.cs b/src/Playwright/API/Generated/Options/PageHoverOptions.cs index 9103936bf3..1369acaba5 100644 --- a/src/Playwright/API/Generated/Options/PageHoverOptions.cs +++ b/src/Playwright/API/Generated/Options/PageHoverOptions.cs @@ -109,7 +109,9 @@ public PageHoverOptions(PageHoverOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/PagePdfOptions.cs b/src/Playwright/API/Generated/Options/PagePdfOptions.cs index f9d9ff2226..1199dd6764 100644 --- a/src/Playwright/API/Generated/Options/PagePdfOptions.cs +++ b/src/Playwright/API/Generated/Options/PagePdfOptions.cs @@ -60,19 +60,14 @@ public PagePdfOptions(PagePdfOptions clone) [JsonPropertyName("displayHeaderFooter")] public bool? DisplayHeaderFooter { get; set; } - /// - /// - /// HTML template for the print footer. Should use the same format as the . - /// - /// + /// HTML template for the print footer. Should use the same format as the . [JsonPropertyName("footerTemplate")] public string? FooterTemplate { get; set; } /// /// - /// Paper format. If set, takes priority over or options. Defaults to 'Letter'. + /// Paper format. If set, takes priority over or + /// options. Defaults to 'Letter'. /// /// [JsonPropertyName("format")] @@ -121,9 +116,9 @@ public PagePdfOptions(PagePdfOptions clone) /// /// - /// The file path to save the PDF to. If is a relative path, - /// then it is resolved relative to the current working directory. If no path is provided, - /// the PDF won't be saved to the disk. + /// The file path to save the PDF to. If is a relative + /// path, then it is resolved relative to the current working directory. If no path + /// is provided, the PDF won't be saved to the disk. /// /// [JsonPropertyName("path")] @@ -132,7 +127,7 @@ public PagePdfOptions(PagePdfOptions clone) /// /// /// Give any CSS @page size declared in the page priority over what is declared - /// in and or + /// in and or /// options. Defaults to false, which will scale the content to fit the paper /// size. /// diff --git a/src/Playwright/API/Generated/Options/PageScreenshotOptions.cs b/src/Playwright/API/Generated/Options/PageScreenshotOptions.cs index 16aa407430..ae97a61407 100644 --- a/src/Playwright/API/Generated/Options/PageScreenshotOptions.cs +++ b/src/Playwright/API/Generated/Options/PageScreenshotOptions.cs @@ -100,7 +100,7 @@ public PageScreenshotOptions(PageScreenshotOptions clone) /// /// /// Specify locators that should be masked when the screenshot is taken. Masked elements - /// will be overlaid with a pink box #FF00FF (customized by ) + /// will be overlaid with a pink box #FF00FF (customized by ) /// that completely covers its bounding box. /// /// @@ -128,9 +128,9 @@ public PageScreenshotOptions(PageScreenshotOptions clone) /// /// /// The file path to save the image to. The screenshot type will be inferred from file - /// extension. If is a relative path, then it is resolved relative - /// to the current working directory. If no path is provided, the image won't be saved - /// to the disk. + /// extension. If is a relative path, then it is + /// resolved relative to the current working directory. If no path is provided, the + /// image won't be saved to the disk. /// /// [JsonPropertyName("path")] diff --git a/src/Playwright/API/Generated/Options/PageTapOptions.cs b/src/Playwright/API/Generated/Options/PageTapOptions.cs index 62f3157698..61a18ecc69 100644 --- a/src/Playwright/API/Generated/Options/PageTapOptions.cs +++ b/src/Playwright/API/Generated/Options/PageTapOptions.cs @@ -109,7 +109,9 @@ public PageTapOptions(PageTapOptions clone) /// /// When set, this method only performs the actionability /// checks and skips the action. Defaults to false. Useful to wait until the - /// element is ready for the action without performing it. + /// element is ready for the action without performing it. Note that keyboard modifiers + /// will be pressed regardless of trial to allow testing elements which are only + /// visible when those keys are pressed. /// /// [JsonPropertyName("trial")] diff --git a/src/Playwright/API/Generated/Options/PageWaitForFunctionOptions.cs b/src/Playwright/API/Generated/Options/PageWaitForFunctionOptions.cs index 5d488ee1f6..c6c7636a62 100644 --- a/src/Playwright/API/Generated/Options/PageWaitForFunctionOptions.cs +++ b/src/Playwright/API/Generated/Options/PageWaitForFunctionOptions.cs @@ -46,7 +46,7 @@ public PageWaitForFunctionOptions(PageWaitForFunctionOptions clone) /// /// /// If specified, then it is treated as an interval in milliseconds at which the function - /// would be executed. By default if the option is not specified + /// would be executed. By default if the option is not specified /// is executed in requestAnimationFrame callback. /// /// diff --git a/src/Playwright/API/Generated/Options/TracingStartChunkOptions.cs b/src/Playwright/API/Generated/Options/TracingStartChunkOptions.cs index c450728dc7..1620c10829 100644 --- a/src/Playwright/API/Generated/Options/TracingStartChunkOptions.cs +++ b/src/Playwright/API/Generated/Options/TracingStartChunkOptions.cs @@ -46,9 +46,10 @@ public TracingStartChunkOptions(TracingStartChunkOptions clone) /// /// /// If specified, intermediate trace files are going to be saved into the files with - /// the given name prefix inside the folder specified in - /// . To specify the final trace zip file name, - /// you need to pass path option to instead. + /// the given name prefix inside the directory + /// specified in . To specify the final trace + /// zip file name, you need to pass path option to + /// instead. /// /// [JsonPropertyName("name")] diff --git a/src/Playwright/API/Generated/Options/TracingStartOptions.cs b/src/Playwright/API/Generated/Options/TracingStartOptions.cs index 57710ac26c..b71df26df4 100644 --- a/src/Playwright/API/Generated/Options/TracingStartOptions.cs +++ b/src/Playwright/API/Generated/Options/TracingStartOptions.cs @@ -49,9 +49,10 @@ public TracingStartOptions(TracingStartOptions clone) /// /// /// If specified, intermediate trace files are going to be saved into the files with - /// the given name prefix inside the folder specified in - /// . To specify the final trace zip file name, - /// you need to pass path option to instead. + /// the given name prefix inside the directory + /// specified in . To specify the final trace + /// zip file name, you need to pass path option to + /// instead. /// /// [JsonPropertyName("name")] diff --git a/src/Playwright/API/Generated/Options/WebSocketRouteCloseOptions.cs b/src/Playwright/API/Generated/Options/WebSocketRouteCloseOptions.cs new file mode 100644 index 0000000000..7eedfc76a1 --- /dev/null +++ b/src/Playwright/API/Generated/Options/WebSocketRouteCloseOptions.cs @@ -0,0 +1,65 @@ +/* + * MIT License + * + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System.Text.Json.Serialization; + +#nullable enable + +namespace Microsoft.Playwright; + +public class WebSocketRouteCloseOptions +{ + public WebSocketRouteCloseOptions() { } + + public WebSocketRouteCloseOptions(WebSocketRouteCloseOptions clone) + { + if (clone == null) + { + return; + } + + Code = clone.Code; + Reason = clone.Reason; + } + + /// + /// + /// Optional close + /// code. + /// + /// + [JsonPropertyName("code")] + public int? Code { get; set; } + + /// + /// + /// Optional close + /// reason. + /// + /// + [JsonPropertyName("reason")] + public string? Reason { get; set; } +} + +#nullable disable diff --git a/src/Playwright/API/Supplements/IWebSocketRoute.cs b/src/Playwright/API/Supplements/IWebSocketRoute.cs new file mode 100644 index 0000000000..d564548f2d --- /dev/null +++ b/src/Playwright/API/Supplements/IWebSocketRoute.cs @@ -0,0 +1,33 @@ +/* + * MIT License + * + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#nullable enable + +namespace Microsoft.Playwright; + +public partial interface IWebSocketRoute +{ + /// + void Send(string message); +} diff --git a/src/Playwright/Core/APIRequestContext.cs b/src/Playwright/Core/APIRequestContext.cs index 3f3aef0d8f..91737ede16 100644 --- a/src/Playwright/Core/APIRequestContext.cs +++ b/src/Playwright/Core/APIRequestContext.cs @@ -29,7 +29,6 @@ using System.Runtime.CompilerServices; using System.Text.Json; using System.Threading.Tasks; -using System.Web; using Microsoft.Playwright.Helpers; using Microsoft.Playwright.Transport; using Microsoft.Playwright.Transport.Protocol; @@ -147,7 +146,8 @@ public async Task FetchAsync(string url, APIRequestContextOptions ["maxRedirects"] = options?.MaxRedirects, ["maxRetries"] = options?.MaxRetries, ["timeout"] = options.Timeout, - ["params"] = QueryParamsToProtocol(options.Params, options.ParamsString), + ["params"] = options.Params?.ToDictionary(x => x.Key, x => x.Value.ToString()).ToProtocol(), + ["encodedParams"] = options.ParamsString, ["headers"] = options.Headers?.ToProtocol(), ["jsonData"] = jsonData, ["postData"] = postData != null ? Convert.ToBase64String(postData) : null, @@ -159,28 +159,6 @@ public async Task FetchAsync(string url, APIRequestContextOptions return new APIResponse(this, response?.GetProperty("response").ToObject()); } - private static IEnumerable QueryParamsToProtocol(IEnumerable> @params, string paramsString) - { - if (@params != null) - { - return @params.ToDictionary(x => x.Key, x => x.Value.ToString()).ToProtocol(); - } - if (string.IsNullOrEmpty(paramsString)) - { - return null; - } - var result = new List(); - var paramsCollection = HttpUtility.ParseQueryString(paramsString); - foreach (var key in paramsCollection.AllKeys) - { - foreach (var value in paramsCollection.GetValues(key)) - { - result.Add(new NameValue { Name = key, Value = value }); - } - } - return result; - } - private bool IsJsonContentType(IDictionary headers) { if (headers == null) diff --git a/src/Playwright/Core/BrowserContext.cs b/src/Playwright/Core/BrowserContext.cs index 9414a42d30..10cf285902 100644 --- a/src/Playwright/Core/BrowserContext.cs +++ b/src/Playwright/Core/BrowserContext.cs @@ -49,6 +49,7 @@ internal class BrowserContext : ChannelOwner, IBrowserContext internal readonly APIRequestContext _request; private readonly Dictionary _harRecorders = new(); internal readonly List _serviceWorkers = new(); + private readonly List _webSocketRoutes = new(); private List _routes = new(); internal readonly List _pages = new(); private readonly Browser _browser; @@ -181,6 +182,10 @@ internal override void OnMessage(string method, JsonElement? serverParams) var route = serverParams?.GetProperty("route").ToObject(_connection.DefaultJsonSerializerOptions); Channel_Route(this, route); break; + case "webSocketRoute": + var webSocketRoute = serverParams?.GetProperty("webSocketRoute").ToObject(_connection.DefaultJsonSerializerOptions); + _ = OnWebSocketRouteAsync(webSocketRoute).ConfigureAwait(false); + break; case "page": Channel_OnPage( this, @@ -696,7 +701,7 @@ internal async Task OnRouteAsync(Route route) try { - await route.InnerContinueAsync(true).ConfigureAwait(false); + await route.InnerContinueAsync(true /* isFallback */).ConfigureAwait(false); } catch { @@ -705,6 +710,19 @@ internal async Task OnRouteAsync(Route route) } } + internal async Task OnWebSocketRouteAsync(WebSocketRoute webSocketRoute) + { + var routeHandler = _webSocketRoutes.Find(r => r.Regex?.IsMatch(webSocketRoute.Url) == true || r.Function?.Invoke(webSocketRoute.Url) == true); + if (routeHandler != null) + { + await routeHandler.HandleAsync(webSocketRoute).ConfigureAwait(false); + } + else + { + webSocketRoute.ConnectToServer(); + } + } + internal bool UrlMatches(string url, string glob) => new Regex(CombineUrlWithBase(glob).GlobToRegex()).Match(url).Success; @@ -892,6 +910,35 @@ private void DisposeHarRouters() } _harRouters.Clear(); } + + [MethodImpl(MethodImplOptions.NoInlining)] + public Task RouteWebSocketAsync(string url, Action handler) + => RouteWebSocketAsync(new Regex(CombineUrlWithBase(url).GlobToRegex()), null, handler); + + [MethodImpl(MethodImplOptions.NoInlining)] + public Task RouteWebSocketAsync(Regex url, Action handler) + => RouteWebSocketAsync(url, null, handler); + + [MethodImpl(MethodImplOptions.NoInlining)] + public Task RouteWebSocketAsync(Func url, Action handler) + => RouteWebSocketAsync(null, url, handler); + + private Task RouteWebSocketAsync(Regex urlRegex, Func urlFunc, Delegate handler) + { + _webSocketRoutes.Insert(0, new WebSocketRouteHandler() + { + Regex = urlRegex, + Function = urlFunc, + Handler = handler, + }); + return UpdateWebSocketInterceptionAsync(); + } + + private async Task UpdateWebSocketInterceptionAsync() + { + var patterns = WebSocketRouteHandler.PrepareInterceptionPatterns(_webSocketRoutes); + await SendMessageToServerAsync("setWebSocketInterceptionPatterns", patterns).ConfigureAwait(false); + } } internal class HarRecorder diff --git a/src/Playwright/Core/LocalUtils.cs b/src/Playwright/Core/LocalUtils.cs index fa8b104d64..cee6fa17ad 100644 --- a/src/Playwright/Core/LocalUtils.cs +++ b/src/Playwright/Core/LocalUtils.cs @@ -37,6 +37,7 @@ internal class LocalUtils : ChannelOwner public LocalUtils(ChannelOwner parent, string guid, LocalUtilsInitializer initializer) : base(parent, guid) { + MarkAsInternalType(); foreach (var entry in initializer.DeviceDescriptors) { _devices[entry.Name] = entry.Descriptor; diff --git a/src/Playwright/Core/Page.cs b/src/Playwright/Core/Page.cs index 0caa169628..3772c8138c 100644 --- a/src/Playwright/Core/Page.cs +++ b/src/Playwright/Core/Page.cs @@ -46,8 +46,8 @@ internal class Page : ChannelOwner, IPage internal readonly List _workers = new(); internal readonly TimeoutSettings _timeoutSettings; private readonly List _harRouters = new(); - // Func Handler private readonly Dictionary _locatorHandlers = new(); + private readonly List _webSocketRoutes = new(); private List _routes = new(); private Video _video; private string _closeReason; @@ -237,6 +237,10 @@ internal override void OnMessage(string method, JsonElement? serverParams) var route = serverParams?.GetProperty("route").ToObject(_connection.DefaultJsonSerializerOptions); Channel_Route(this, route); break; + case "webSocketRoute": + var webSocketRoute = serverParams?.GetProperty("webSocketRoute").ToObject(_connection.DefaultJsonSerializerOptions); + _ = OnWebSocketRouteAsync(webSocketRoute).ConfigureAwait(false); + break; case "popup": Popup?.Invoke(this, serverParams?.GetProperty("page").ToObject(_connection.DefaultJsonSerializerOptions)); break; @@ -294,6 +298,9 @@ public IFrame Frame(string name) [MethodImpl(MethodImplOptions.NoInlining)] public Task OpenerAsync() => Task.FromResult(Opener?.IsClosed == false ? Opener : null); + [MethodImpl(MethodImplOptions.NoInlining)] + public Task RequestGCAsync() => SendMessageToServerAsync("requestGC"); + [MethodImpl(MethodImplOptions.NoInlining)] public Task EmulateMediaAsync(PageEmulateMediaOptions options = default) { @@ -1343,6 +1350,19 @@ private async Task OnRouteAsync(Route route) await Context.OnRouteAsync(route).ConfigureAwait(false); } + private async Task OnWebSocketRouteAsync(WebSocketRoute webSocketRoute) + { + var routeHandler = _webSocketRoutes.Find(r => r.Regex?.IsMatch(webSocketRoute.Url) == true || r.Function?.Invoke(webSocketRoute.Url) == true); + if (routeHandler != null) + { + await routeHandler.HandleAsync(webSocketRoute).ConfigureAwait(false); + } + else + { + await Context.OnWebSocketRouteAsync(webSocketRoute).ConfigureAwait(false); + } + } + private void Channel_FrameDetached(object sender, IFrame args) { var frame = (Frame)args; @@ -1562,6 +1582,35 @@ public async Task RemoveLocatorHandlerAsync(ILocator locator) } } } + + [MethodImpl(MethodImplOptions.NoInlining)] + public Task RouteWebSocketAsync(string url, Action handler) + => RouteWebSocketAsync(new Regex(Context.CombineUrlWithBase(url).GlobToRegex()), null, handler); + + [MethodImpl(MethodImplOptions.NoInlining)] + public Task RouteWebSocketAsync(Regex url, Action handler) + => RouteWebSocketAsync(url, null, handler); + + [MethodImpl(MethodImplOptions.NoInlining)] + public Task RouteWebSocketAsync(Func url, Action handler) + => RouteWebSocketAsync(null, url, handler); + + private Task RouteWebSocketAsync(Regex urlRegex, Func urlFunc, Delegate handler) + { + _webSocketRoutes.Insert(0, new WebSocketRouteHandler() + { + Regex = urlRegex, + Function = urlFunc, + Handler = handler, + }); + return UpdateWebSocketInterceptionAsync(); + } + + private async Task UpdateWebSocketInterceptionAsync() + { + var patterns = WebSocketRouteHandler.PrepareInterceptionPatterns(_webSocketRoutes); + await SendMessageToServerAsync("setWebSocketInterceptionPatterns", patterns).ConfigureAwait(false); + } } internal class LocatorHandler diff --git a/src/Playwright/Core/Route.cs b/src/Playwright/Core/Route.cs index 89b531c871..0ac2ef1561 100644 --- a/src/Playwright/Core/Route.cs +++ b/src/Playwright/Core/Route.cs @@ -43,10 +43,11 @@ internal class Route : ChannelOwner, IRoute { private readonly RouteInitializer _initializer; private TaskCompletionSource _handlingTask; - internal bool DidThrow; + internal bool _didThrow; internal Route(ChannelOwner parent, string guid, RouteInitializer initializer) : base(parent, guid) { + MarkAsInternalType(); _initializer = initializer; _request = initializer.Request; } @@ -71,7 +72,6 @@ public async Task FulfillAsync(RouteFulfillOptions options = default) options.Json, options.Path, options.Response).ConfigureAwait(false); - normalized["requestUrl"] = _request._initializer.Url; await RaceWithTargetCloseAsync(SendMessageToServerAsync("fulfill", normalized)).ConfigureAwait(false); ReportHandled(true); } @@ -86,7 +86,6 @@ await RaceWithTargetCloseAsync(SendMessageToServerAsync( "abort", new Dictionary { - ["requestUrl"] = _request._initializer.Url, ["errorCode"] = string.IsNullOrEmpty(errorCode) ? RequestAbortErrorCode.Failed : errorCode, })).ConfigureAwait(false); }).ConfigureAwait(false); @@ -97,27 +96,24 @@ public async Task ContinueAsync(RouteContinueOptions options = default) { CheckNotHandled(); _request.ApplyFallbackOverrides(new RouteFallbackOptions().FromRouteContinueOptions(options)); - await InnerContinueAsync().ConfigureAwait(false); + await InnerContinueAsync(false /* isFallback */).ConfigureAwait(false); ReportHandled(true); } - internal async Task InnerContinueAsync(bool @internal = false) + internal async Task InnerContinueAsync(bool isFallback = false) { var options = _request.FallbackOverridesForContinue(); - await _connection.WrapApiCallAsync( - () => RaceWithTargetCloseAsync( - SendMessageToServerAsync( + await RaceWithTargetCloseAsync( + SendMessageToServerAsync( "continue", new Dictionary { - ["requestUrl"] = _request._initializer.Url, ["url"] = options.Url, ["method"] = options.Method, ["postData"] = options.PostData != null ? Convert.ToBase64String(options.PostData) : null, ["headers"] = options.Headers?.Select(kv => new HeaderEntry { Name = kv.Key, Value = kv.Value }).ToArray(), - ["isFallback"] = @internal, - })), - @internal).ConfigureAwait(false); + ["isFallback"] = isFallback, + })).ConfigureAwait(false); } private async Task HandleRouteAsync(Func callback) @@ -130,7 +126,7 @@ private async Task HandleRouteAsync(Func callback) } catch (Exception) { - DidThrow = true; + _didThrow = true; throw; } } diff --git a/src/Playwright/Core/RouteHandler.cs b/src/Playwright/Core/RouteHandler.cs index 8002bcb7c7..132917562e 100644 --- a/src/Playwright/Core/RouteHandler.cs +++ b/src/Playwright/Core/RouteHandler.cs @@ -70,16 +70,19 @@ public static Dictionary PrepareInterceptionPatterns(List(); - allPattern["glob"] = "**/*"; + var allPattern = new Dictionary + { + ["glob"] = "**/*", + }; patterns.Clear(); patterns.Add(allPattern); } - var result = new Dictionary(); - result["patterns"] = patterns; - return result; + return new Dictionary + { + ["patterns"] = patterns, + }; } public async Task HandleAsync(Route route) @@ -125,7 +128,7 @@ public async Task StopAsync(UnrouteBehavior behavior) var tasks = new List(); foreach (var activation in _activeInvocations.Keys) { - if (!activation.Route.DidThrow) + if (!activation.Route._didThrow) { tasks.Add(activation.Complete.Task); } diff --git a/src/Playwright/Core/Tracing.cs b/src/Playwright/Core/Tracing.cs index ff34ccd2f4..01c99f1dd0 100644 --- a/src/Playwright/Core/Tracing.cs +++ b/src/Playwright/Core/Tracing.cs @@ -48,26 +48,21 @@ public async Task StartAsync( TracingStartOptions options = default) { _includeSources = options?.Sources == true; - var traceName = await _connection.WrapApiCallAsync( - async () => + await SendMessageToServerAsync( + "tracingStart", + new Dictionary { - await SendMessageToServerAsync( - "tracingStart", - new Dictionary - { - ["name"] = options?.Name, - ["title"] = options?.Title, - ["screenshots"] = options?.Screenshots, - ["snapshots"] = options?.Snapshots, - ["sources"] = options?.Sources, - }).ConfigureAwait(false); - return (await SendMessageToServerAsync("tracingStartChunk", new Dictionary - { - ["title"] = options?.Title, - ["name"] = options?.Name, - }).ConfigureAwait(false))?.GetProperty("traceName").ToString(); - }, - true).ConfigureAwait(false); + ["name"] = options?.Name, + ["title"] = options?.Title, + ["screenshots"] = options?.Screenshots, + ["snapshots"] = options?.Snapshots, + ["sources"] = options?.Sources, + }).ConfigureAwait(false); + var traceName = (await SendMessageToServerAsync("tracingStartChunk", new Dictionary + { + ["title"] = options?.Title, + ["name"] = options?.Name, + }).ConfigureAwait(false))?.GetProperty("traceName").ToString(); await StartCollectingStacksAsync(traceName).ConfigureAwait(false); } @@ -93,18 +88,13 @@ private async Task StartCollectingStacksAsync(string traceName) } [MethodImpl(MethodImplOptions.NoInlining)] - public Task StopChunkAsync(TracingStopChunkOptions options = default) => _connection.WrapApiCallAsync(() => DoStopChunkAsync(filePath: options?.Path), true); + public Task StopChunkAsync(TracingStopChunkOptions options = default) => DoStopChunkAsync(filePath: options?.Path); [MethodImpl(MethodImplOptions.NoInlining)] public async Task StopAsync(TracingStopOptions options = default) { - await _connection.WrapApiCallAsync( - async () => - { - await StopChunkAsync(new() { Path = options?.Path }).ConfigureAwait(false); - await SendMessageToServerAsync("tracingStop").ConfigureAwait(false); - }, - true).ConfigureAwait(false); + await StopChunkAsync(new() { Path = options?.Path }).ConfigureAwait(false); + await SendMessageToServerAsync("tracingStop").ConfigureAwait(false); } private async Task DoStopChunkAsync(string filePath) diff --git a/src/Playwright/Core/WebSocketRoute.cs b/src/Playwright/Core/WebSocketRoute.cs new file mode 100644 index 0000000000..c69c434dd5 --- /dev/null +++ b/src/Playwright/Core/WebSocketRoute.cs @@ -0,0 +1,236 @@ +/* + * MIT License + * + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading.Tasks; +using Microsoft.Playwright.Helpers; +using Microsoft.Playwright.Transport; +using Microsoft.Playwright.Transport.Protocol; + +namespace Microsoft.Playwright.Core; + +/// +/// . +/// +internal class WebSocketRoute : ChannelOwner, IWebSocketRoute +{ + private readonly IWebSocketRoute _server; + internal readonly WebSocketRouteInitializer _initializer; + private bool _connected; + private Action _onPageClose; + private Action _onPageMessage; + internal Action _onServerClose; + internal Action _onServerMessage; + + internal WebSocketRoute(ChannelOwner parent, string guid, WebSocketRouteInitializer initializer) : base(parent, guid) + { + _initializer = initializer; + MarkAsInternalType(); + _server = new ServerWebSocketRoute(this); + } + + public string Url => _initializer.Url; + + internal override void OnMessage(string method, JsonElement? serverParams) + { + switch (method) + { + case "messageFromPage": + if (_onPageMessage != null) + { + var frame = new WebSocketFrame(serverParams.Value.GetProperty("message").GetString(), serverParams.Value.GetProperty("isBase64").GetBoolean()); + _onPageMessage(frame); + } + else if (_connected) + { + SendMessageToServerAsync("sendToServer", new Dictionary + { + ["message"] = serverParams?.GetProperty("message").GetString(), + ["isBase64"] = serverParams?.GetProperty("isBase64").GetBoolean(), + }).IgnoreException(); + } + break; + case "messageFromServer": + if (_onServerMessage != null) + { + var frame = new WebSocketFrame(serverParams.Value.GetProperty("message").GetString(), serverParams.Value.GetProperty("isBase64").GetBoolean()); + _onServerMessage(frame); + } + else + { + SendMessageToServerAsync("sendToPage", new Dictionary + { + ["message"] = serverParams?.GetProperty("message").GetString(), + ["isBase64"] = serverParams?.GetProperty("isBase64").GetBoolean(), + }).IgnoreException(); + } + break; + case "closePage": + if (_onPageClose != null) + { + _onPageClose(serverParams?.GetProperty("code").GetInt32(), serverParams?.GetProperty("reason").GetString()); + } + else + { + SendMessageToServerAsync("closeServer", new Dictionary + { + ["code"] = serverParams?.GetProperty("code").GetInt32(), + ["reason"] = serverParams?.GetProperty("reason").GetString(), + ["wasClean"] = serverParams?.GetProperty("wasClean").GetBoolean(), + }).IgnoreException(); + } + break; + case "closeServer": + if (_onServerClose != null) + { + _onServerClose(serverParams?.GetProperty("code").GetInt32(), serverParams?.GetProperty("reason").GetString()); + } + else + { + SendMessageToServerAsync("closePage", new Dictionary + { + ["code"] = serverParams?.GetProperty("code").GetInt32(), + ["reason"] = serverParams?.GetProperty("reason").GetString(), + ["wasClean"] = serverParams?.GetProperty("wasClean").GetBoolean(), + }).IgnoreException(); + } + break; + } + } + + public async Task CloseAsync(WebSocketRouteCloseOptions options = null) + { + try + { + await SendMessageToServerAsync("closePage", new Dictionary + { + ["code"] = options?.Code, + ["reason"] = options?.Reason, + ["wasClean"] = true, + }).ConfigureAwait(false); + } + catch (Exception) + { + // Ignore the exception + } + } + + public void Send(byte[] message) + { + SendMessageToServerAsync("sendToPage", new Dictionary + { + ["message"] = Convert.ToBase64String(message), + ["isBase64"] = true, + }).IgnoreException(); + } + + public void Send(string message) + { + SendMessageToServerAsync("sendToPage", new Dictionary + { + ["message"] = message, + ["isBase64"] = false, + }).IgnoreException(); + } + + public IWebSocketRoute ConnectToServer() + { + if (_connected) + { + throw new PlaywrightException("Already connected to the server"); + } + _connected = true; + SendMessageToServerAsync("connect").IgnoreException(); + return _server; + } + + public void OnMessage(Action handler) => _onPageMessage = handler; + + public void OnClose(Action handler) => _onPageClose = handler; + + internal async Task AfterHandleAsync() + { + if (_connected) + { + return; + } + // Ensure that websocket is "open" and can send messages without an actual server connection. + await SendMessageToServerAsync("ensureOpened").ConfigureAwait(false); + } +} + +internal class ServerWebSocketRoute : IWebSocketRoute +{ + private readonly WebSocketRoute _webSocketRoute; + + internal ServerWebSocketRoute(WebSocketRoute route) + { + _webSocketRoute = route; + } + + public string Url => _webSocketRoute._initializer.Url; + + public async Task CloseAsync(WebSocketRouteCloseOptions options = null) + { + try + { + await _webSocketRoute.SendMessageToServerAsync("closeServer", new Dictionary + { + ["code"] = options?.Code, + ["reason"] = options?.Reason, + ["wasClean"] = true, + }).ConfigureAwait(false); + } + catch (Exception) + { + // Ignore the exception + } + } + + public IWebSocketRoute ConnectToServer() => throw new PlaywrightException("ConnectToServer() must be called on the page-side WebSocketRoute"); + + public void OnClose(Action handler) => _webSocketRoute._onServerClose = handler; + + public void OnMessage(Action handler) => _webSocketRoute._onServerMessage = handler; + + public void Send(byte[] message) + { + _webSocketRoute.SendMessageToServerAsync("sendToServer", new Dictionary + { + ["message"] = Convert.ToBase64String(message), + ["isBase64"] = true, + }).IgnoreException(); + } + + public void Send(string message) + { + _webSocketRoute.SendMessageToServerAsync("sendToServer", new Dictionary + { + ["message"] = message, + ["isBase64"] = false, + }).IgnoreException(); + } +} diff --git a/src/Playwright/Core/WebSocketRouteHandler.cs b/src/Playwright/Core/WebSocketRouteHandler.cs new file mode 100644 index 0000000000..3e8add3f1d --- /dev/null +++ b/src/Playwright/Core/WebSocketRouteHandler.cs @@ -0,0 +1,88 @@ +/* + * MIT License + * + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.Playwright.Helpers; + +namespace Microsoft.Playwright.Core; + +internal class WebSocketRouteHandler +{ + public Regex Regex { get; set; } + + public Func Function { get; set; } + + public Delegate Handler { get; set; } + + public static Dictionary PrepareInterceptionPatterns(List handlers) + { + bool all = false; + var patterns = new List>(); + foreach (var handler in handlers) + { + var pattern = new Dictionary(); + patterns.Add(pattern); + + if (handler.Regex != null) + { + pattern["regexSource"] = handler.Regex.ToString(); + pattern["regexFlags"] = handler.Regex.Options.GetInlineFlags(); + } + + if (handler.Function != null) + { + all = true; + } + } + + if (all) + { + var allPattern = new Dictionary + { + ["glob"] = "**/*", + }; + + patterns.Clear(); + patterns.Add(allPattern); + } + + return new Dictionary + { + ["patterns"] = patterns, + }; + } + + public async Task HandleAsync(WebSocketRoute route) + { + var maybeTask = Handler.DynamicInvoke(new object[] { route }); + if (maybeTask is Task task) + { + await task.ConfigureAwait(false); + } + await route.AfterHandleAsync().ConfigureAwait(false); + } +} diff --git a/src/Playwright/Transport/ChannelOwner.cs b/src/Playwright/Transport/ChannelOwner.cs index bd4972cd03..06e23eb521 100644 --- a/src/Playwright/Transport/ChannelOwner.cs +++ b/src/Playwright/Transport/ChannelOwner.cs @@ -37,6 +37,7 @@ internal class ChannelOwner internal readonly Connection _connection; internal bool _wasCollected; + internal bool _isInternalType; internal ChannelOwner(ChannelOwner parent, string guid) : this(parent, null, guid) { @@ -93,6 +94,8 @@ internal void DisposeOwner(string reason) [MethodImpl(MethodImplOptions.NoInlining)] public Task WrapApiBoundaryAsync(Func action) => _connection.WrapApiBoundaryAsync(action); + internal void MarkAsInternalType() => _isInternalType = true; + internal EventHandler UpdateEventHandler(string eventName, EventHandler handlers, EventHandler handler, bool add) { if (add) diff --git a/src/Playwright/Transport/Channels/ChannelOwnerType.cs b/src/Playwright/Transport/Channels/ChannelOwnerType.cs index a6aefc3f9d..adce04d785 100644 --- a/src/Playwright/Transport/Channels/ChannelOwnerType.cs +++ b/src/Playwright/Transport/Channels/ChannelOwnerType.cs @@ -116,4 +116,7 @@ internal enum ChannelOwnerType [EnumMember(Value = "APIRequestContext")] APIRequestContext, + + [EnumMember(Value = "WebSocketroute")] + WebSocketRoute, } diff --git a/src/Playwright/Transport/Connection.cs b/src/Playwright/Transport/Connection.cs index af1d1a6f43..b754f76d9a 100644 --- a/src/Playwright/Transport/Connection.cs +++ b/src/Playwright/Transport/Connection.cs @@ -184,7 +184,7 @@ private async Task InnerSendMessageToServerAsync( }; } - if (_tracingCount > 0 && frames.Count > 0 && @object.Guid != "localUtils") + if (_tracingCount > 0 && frames.Count > 0 && !@object._isInternalType) { LocalUtils.AddStackToTracingNoReply(frames, id); } @@ -386,6 +386,9 @@ private ChannelOwner CreateRemoteObject(string parentGuid, ChannelOwnerType type case ChannelOwnerType.WebSocket: result = new WebSocket(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); break; + case ChannelOwnerType.WebSocketRoute: + result = new WebSocketRoute(parent, guid, initializer?.ToObject(DefaultJsonSerializerOptions)); + break; case ChannelOwnerType.Selectors: result = new Selectors(parent, guid); break; diff --git a/src/Playwright/Transport/Protocol/Generated/PlaywrightInitializer.cs b/src/Playwright/Transport/Protocol/Generated/PlaywrightInitializer.cs index ce4b7799b0..c9424027fb 100644 --- a/src/Playwright/Transport/Protocol/Generated/PlaywrightInitializer.cs +++ b/src/Playwright/Transport/Protocol/Generated/PlaywrightInitializer.cs @@ -31,15 +31,18 @@ internal class PlaywrightInitializer [JsonPropertyName("chromium")] public Core.BrowserType Chromium { get; set; } - [JsonPropertyName("bidi")] - public Core.BrowserType Bidi { get; set; } - [JsonPropertyName("firefox")] public Core.BrowserType Firefox { get; set; } [JsonPropertyName("webkit")] public Core.BrowserType Webkit { get; set; } + [JsonPropertyName("bidiChromium")] + public Core.BrowserType BidiChromium { get; set; } + + [JsonPropertyName("bidiFirefox")] + public Core.BrowserType BidiFirefox { get; set; } + [JsonPropertyName("utils")] public Core.LocalUtils Utils { get; set; } diff --git a/src/Playwright/Transport/Protocol/Generated/WebSocketRouteInitializer.cs b/src/Playwright/Transport/Protocol/Generated/WebSocketRouteInitializer.cs new file mode 100644 index 0000000000..c741609f6c --- /dev/null +++ b/src/Playwright/Transport/Protocol/Generated/WebSocketRouteInitializer.cs @@ -0,0 +1,33 @@ +/* + * MIT License + * + * Copyright (c) Microsoft Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +using System.Text.Json.Serialization; + +namespace Microsoft.Playwright.Transport.Protocol; + +internal class WebSocketRouteInitializer +{ + [JsonPropertyName("url")] + public string Url { get; set; } +}