diff --git a/README.md b/README.md index 69a4b1a419..3cd80a7ea3 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 128.0.6613.18 | ✅ | ✅ | ✅ | +| Chromium 129.0.6668.29 | ✅ | ✅ | ✅ | | WebKit 18.0 | ✅ | ✅ | ✅ | -| Firefox 128.0 | ✅ | ✅ | ✅ | +| Firefox 130.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 3a88e78668..c1062d38e4 100644 --- a/src/Common/Version.props +++ b/src/Common/Version.props @@ -2,7 +2,7 @@ 1.46.0 $(AssemblyVersion) - 1.46.0 + 1.47.0 $(AssemblyVersion) $(AssemblyVersion) true diff --git a/src/Playwright.Tests/BrowserContextFetchTests.cs b/src/Playwright.Tests/BrowserContextFetchTests.cs index c019c8f221..770c4e891b 100644 --- a/src/Playwright.Tests/BrowserContextFetchTests.cs +++ b/src/Playwright.Tests/BrowserContextFetchTests.cs @@ -103,22 +103,35 @@ public async Task ShouldAddSessionCookiesToRequest() Assert.AreEqual("username=John Doe", cookieHeader); } - [PlaywrightTest("browsercontext-fetch.spec.ts", "should support queryParams")] - public async Task ShouldSupportQueryParams() + [PlaywrightTest("browsercontext-fetch.spec.ts", "should support paras passed as object")] + public async Task ShouldSupportParamsPassedAsObject() { - var queryString = new QueryString(); - queryString.Add("p1", "v1"); - queryString.Add("парам2", "знач2"); - var requestOptions = new APIRequestContextOptions() { Params = new Dictionary() { { "p1", "v1" }, { "парам2", "знач2" } } }; + var requestOptions = new APIRequestContextOptions() { Params = new Dictionary() { { "param1", "value1" }, { "парам2", "знач2" } } }; await ForAllMethods(Context.APIRequest, async responseTask => { var (receivedQueryString, _) = await TaskUtils.WhenAll( Server.WaitForRequest("/empty.html", request => request.Query), responseTask ); - Assert.AreEqual("v1", receivedQueryString["p1"].First()); + CollectionAssert.AreEquivalent(new[] { "value1", "value2" }, receivedQueryString["param1"]); Assert.AreEqual("знач2", receivedQueryString["парам2"].First()); - }, Server.Prefix + "/empty.html?" + queryString.ToString(), requestOptions); + }, Server.Prefix + "/empty.html?param1=value2", requestOptions); + } + + [PlaywrightTest("browsercontext-fetch.spec.ts", "should support params passed as string")] + public async Task ShouldSupportParamsPassedAsString() + { + var queryParams = "?param1=value1¶m1=value2&парам2=знач2"; + var requestOptions = new APIRequestContextOptions() { ParamsString = queryParams }; + await ForAllMethods(Context.APIRequest, async responseTask => + { + var (receivedQueryString, _) = await TaskUtils.WhenAll( + Server.WaitForRequest("/empty.html", request => request.Query), + responseTask + ); + CollectionAssert.AreEquivalent(new[] { "value1", "value2" }, receivedQueryString["param1"]); + Assert.AreEqual("знач2", string.Join(",", receivedQueryString["парам2"])); + }, Server.Prefix + "/empty.html", requestOptions); } [PlaywrightTest("browsercontext-fetch.spec.ts", "should support failOnStatusCode")] diff --git a/src/Playwright.Tests/ClientCertficatesTests.cs b/src/Playwright.Tests/ClientCertficatesTests.cs index 723fc405fc..ddc43c853a 100644 --- a/src/Playwright.Tests/ClientCertficatesTests.cs +++ b/src/Playwright.Tests/ClientCertficatesTests.cs @@ -147,6 +147,40 @@ public async Task ShouldWorkWithNewContext() await context.CloseAsync(); } + [PlaywrightTest("", "")] + public async Task ShouldWorkWithNewContextAndCertificateAsContent() + { + var context = await Browser.NewContextAsync(new() + { + IgnoreHTTPSErrors = true, + ClientCertificates = + [ + new() + { + Origin = "https://localhost:10000", + Cert = File.ReadAllBytes(TestUtils.GetAsset("client-certificates/client/trusted/cert.pem")), + Key = File.ReadAllBytes(TestUtils.GetAsset("client-certificates/client/trusted/key.pem")), + } + ] + }); + var page = await context.NewPageAsync(); + { + await page.GotoAsync("https://127.0.0.1:10000"); + await Expect(page.GetByTestId("message")).ToHaveTextAsync("Sorry, but you need to provide a client certificate to continue."); + + var response = await page.APIRequest.GetAsync("https://127.0.0.1:10000"); + StringAssert.Contains("Sorry, but you need to provide a client certificate to continue", await response.TextAsync()); + } + { + await page.GotoAsync("https://localhost:10000"); + await Expect(page.GetByText("Hello CN=Alice")).ToBeVisibleAsync(); + + var response = await page.APIRequest.GetAsync("https://localhost:10000"); + StringAssert.Contains("Hello CN=Alice", await response.TextAsync()); + } + await context.CloseAsync(); + } + [PlaywrightTest("", "")] public async Task ShouldWorkWithNewPage() { diff --git a/src/Playwright/API/Generated/IAPIResponse.cs b/src/Playwright/API/Generated/IAPIResponse.cs index 9c8a723587..291e0eb6c0 100644 --- a/src/Playwright/API/Generated/IAPIResponse.cs +++ b/src/Playwright/API/Generated/IAPIResponse.cs @@ -46,7 +46,7 @@ public partial interface IAPIResponse /// /// - /// An array with all the request HTTP headers associated with this response. Header + /// An array with all the response HTTP headers associated with this response. Header /// names are not lower-cased. Headers with multiple entries, such as Set-Cookie, /// appear in the array multiple times. /// diff --git a/src/Playwright/API/Generated/IBrowserContext.cs b/src/Playwright/API/Generated/IBrowserContext.cs index 180e29be1f..d0e3146a1a 100644 --- a/src/Playwright/API/Generated/IBrowserContext.cs +++ b/src/Playwright/API/Generated/IBrowserContext.cs @@ -38,8 +38,8 @@ namespace Microsoft.Playwright; /// belong to the parent page's browser context. /// /// -/// Playwright allows creating "incognito" browser contexts with -/// method. "Incognito" browser contexts don't write any browsing data to disk. +/// Playwright allows creating isolated non-persistent browser contexts with +/// method. Non-persistent browser contexts don't write any browsing data to disk. /// /// /// using var playwright = await Playwright.CreateAsync();
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/IRoute.cs b/src/Playwright/API/Generated/IRoute.cs index 6fb753af83..55fddac265 100644 --- a/src/Playwright/API/Generated/IRoute.cs +++ b/src/Playwright/API/Generated/IRoute.cs @@ -73,7 +73,7 @@ public partial interface IRoute Task AbortAsync(string? errorCode = default); /// - /// Continues route's request with optional overrides. + /// Sends route's request to the network with optional overrides. /// **Usage** /// /// await page.RouteAsync("**/*", async route =>
@@ -91,19 +91,29 @@ public partial interface IRoute /// through redirects, use the combination of and instead. /// + /// + /// will immediately send the request to the network, + /// other matching handlers won't be invoked. Use + /// If you want next matching handler in the chain to be invoked. + /// ///
/// Call options Task ContinueAsync(RouteContinueOptions? options = default); /// /// + /// Continues route's request with optional overrides. The method is similar to with the difference that other matching handlers will + /// be invoked before sending the request. + /// + /// **Usage** + /// /// When several routes match the given pattern, they run in the order opposite to their /// registration. That way the last registered route can always override all the previous /// ones. In the example below, request will be handled by the bottom-most handler first, /// then it'll fall back to the previous one and in the end will be aborted by the first /// registered route. /// - /// **Usage** /// /// await page.RouteAsync("**/*", route => {
/// // Runs last.
@@ -158,6 +168,10 @@ public partial interface IRoute /// await route.FallbackAsync(new() { Headers = headers });
/// }); ///
+ /// + /// Use to immediately send the request to the network, + /// other matching handlers won't be invoked in that case. + /// ///
/// Call options Task FallbackAsync(RouteFallbackOptions? options = default); diff --git a/src/Playwright/API/Generated/Options/APIRequestContextOptions.cs b/src/Playwright/API/Generated/Options/APIRequestContextOptions.cs index b8e5fe8179..b360547f3e 100644 --- a/src/Playwright/API/Generated/Options/APIRequestContextOptions.cs +++ b/src/Playwright/API/Generated/Options/APIRequestContextOptions.cs @@ -53,6 +53,7 @@ public APIRequestContextOptions(APIRequestContextOptions clone) Method = clone.Method; Multipart = clone.Multipart; Params = clone.Params; + ParamsString = clone.ParamsString; Timeout = clone.Timeout; } @@ -181,6 +182,10 @@ public APIRequestContextOptions(APIRequestContextOptions clone) [JsonPropertyName("params")] public IEnumerable>? Params { get; set; } + /// Query parameters to be sent with the URL. + [JsonPropertyName("paramsString")] + public string? ParamsString { get; set; } + /// /// /// Request timeout in milliseconds. Defaults to 30000 (30 seconds). Pass 0 diff --git a/src/Playwright/API/Generated/Options/APIRequestNewContextOptions.cs b/src/Playwright/API/Generated/Options/APIRequestNewContextOptions.cs index 056d36e8d5..d70da7d624 100644 --- a/src/Playwright/API/Generated/Options/APIRequestNewContextOptions.cs +++ b/src/Playwright/API/Generated/Options/APIRequestNewContextOptions.cs @@ -83,11 +83,12 @@ public APIRequestNewContextOptions(APIRequestNewContextOptions clone) /// /// **Details** /// - /// An array of client certificates to be used. Each certificate object must have both - /// certPath and keyPath or a single pfxPath to load the client - /// certificate. Optionally, passphrase property should be provided if the certficiate - /// is encrypted. The origin property should be provided with an exact match - /// to the request origin that the certificate is valid for. + /// An array of client certificates to be used. Each certificate object must have either + /// both certPath and keyPath, a single pfxPath, or their corresponding + /// direct value equivalents (cert and key, or pfx). Optionally, + /// passphrase property should be provided if the certificate is encrypted. The + /// origin property should be provided with an exact match to the request origin + /// that the certificate is valid for. /// /// /// diff --git a/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs b/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs index 68709e811b..321698416a 100644 --- a/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs @@ -124,11 +124,12 @@ public BrowserNewContextOptions(BrowserNewContextOptions clone) /// /// **Details** /// - /// An array of client certificates to be used. Each certificate object must have both - /// certPath and keyPath or a single pfxPath to load the client - /// certificate. Optionally, passphrase property should be provided if the certficiate - /// is encrypted. The origin property should be provided with an exact match - /// to the request origin that the certificate is valid for. + /// An array of client certificates to be used. Each certificate object must have either + /// both certPath and keyPath, a single pfxPath, or their corresponding + /// direct value equivalents (cert and key, or pfx). Optionally, + /// passphrase property should be provided if the certificate is encrypted. The + /// origin property should be provided with an exact match to the request origin + /// that the certificate is valid for. /// /// /// @@ -259,14 +260,6 @@ public BrowserNewContextOptions(BrowserNewContextOptions clone) public IEnumerable? Permissions { get; set; } /// Network proxy settings to use with this context. Defaults to none. - /// - /// - /// For Chromium on Windows the browser needs to be launched with the global proxy for - /// this option to work. If all contexts override the proxy, global proxy will be never - /// used and can be any string, for example launch({ proxy: { server: 'http://per-context' - /// } }). - /// - /// [JsonPropertyName("proxy")] public Proxy? Proxy { get; set; } diff --git a/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs b/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs index f9e9d42159..73a6fd1d49 100644 --- a/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs @@ -124,11 +124,12 @@ public BrowserNewPageOptions(BrowserNewPageOptions clone) /// /// **Details** /// - /// An array of client certificates to be used. Each certificate object must have both - /// certPath and keyPath or a single pfxPath to load the client - /// certificate. Optionally, passphrase property should be provided if the certficiate - /// is encrypted. The origin property should be provided with an exact match - /// to the request origin that the certificate is valid for. + /// An array of client certificates to be used. Each certificate object must have either + /// both certPath and keyPath, a single pfxPath, or their corresponding + /// direct value equivalents (cert and key, or pfx). Optionally, + /// passphrase property should be provided if the certificate is encrypted. The + /// origin property should be provided with an exact match to the request origin + /// that the certificate is valid for. /// /// /// @@ -259,14 +260,6 @@ public BrowserNewPageOptions(BrowserNewPageOptions clone) public IEnumerable? Permissions { get; set; } /// Network proxy settings to use with this context. Defaults to none. - /// - /// - /// For Chromium on Windows the browser needs to be launched with the global proxy for - /// this option to work. If all contexts override the proxy, global proxy will be never - /// used and can be any string, for example launch({ proxy: { server: 'http://per-context' - /// } }). - /// - /// [JsonPropertyName("proxy")] public Proxy? Proxy { get; set; } diff --git a/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs b/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs index 730fb24f76..3c5aaff578 100644 --- a/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs @@ -164,11 +164,12 @@ public BrowserTypeLaunchPersistentContextOptions(BrowserTypeLaunchPersistentCont /// /// **Details** /// - /// An array of client certificates to be used. Each certificate object must have both - /// certPath and keyPath or a single pfxPath to load the client - /// certificate. Optionally, passphrase property should be provided if the certficiate - /// is encrypted. The origin property should be provided with an exact match - /// to the request origin that the certificate is valid for. + /// An array of client certificates to be used. Each certificate object must have either + /// both certPath and keyPath, a single pfxPath, or their corresponding + /// direct value equivalents (cert and key, or pfx). Optionally, + /// passphrase property should be provided if the certificate is encrypted. The + /// origin property should be provided with an exact match to the request origin + /// that the certificate is valid for. /// /// /// diff --git a/src/Playwright/API/Generated/Options/ElementHandleSelectOptionOptions.cs b/src/Playwright/API/Generated/Options/ElementHandleSelectOptionOptions.cs index b8cc4c0e09..ecdfdc3739 100644 --- a/src/Playwright/API/Generated/Options/ElementHandleSelectOptionOptions.cs +++ b/src/Playwright/API/Generated/Options/ElementHandleSelectOptionOptions.cs @@ -54,13 +54,8 @@ public ElementHandleSelectOptionOptions(ElementHandleSelectOptionOptions clone) public bool? Force { get; set; } /// - /// **DEPRECATED** This option will default to true in the future. - /// - /// Actions that initiate navigations are waiting for these navigations to happen and - /// for pages to start loading. You can opt out of waiting via setting this flag. You - /// would only need this option in the exceptional cases such as navigating to inaccessible - /// pages. Defaults to false. - /// + /// **DEPRECATED** This option has no effect. + /// This option has no effect. /// [JsonPropertyName("noWaitAfter")] [System.Obsolete] diff --git a/src/Playwright/API/Generated/Options/FrameSelectOptionOptions.cs b/src/Playwright/API/Generated/Options/FrameSelectOptionOptions.cs index d6c6fc734b..83d2026720 100644 --- a/src/Playwright/API/Generated/Options/FrameSelectOptionOptions.cs +++ b/src/Playwright/API/Generated/Options/FrameSelectOptionOptions.cs @@ -55,13 +55,8 @@ public FrameSelectOptionOptions(FrameSelectOptionOptions clone) public bool? Force { get; set; } /// - /// **DEPRECATED** This option will default to true in the future. - /// - /// Actions that initiate navigations are waiting for these navigations to happen and - /// for pages to start loading. You can opt out of waiting via setting this flag. You - /// would only need this option in the exceptional cases such as navigating to inaccessible - /// pages. Defaults to false. - /// + /// **DEPRECATED** This option has no effect. + /// This option has no effect. /// [JsonPropertyName("noWaitAfter")] [System.Obsolete] diff --git a/src/Playwright/API/Generated/Options/LocatorSelectOptionOptions.cs b/src/Playwright/API/Generated/Options/LocatorSelectOptionOptions.cs index f3b6bfea2a..2b31d05790 100644 --- a/src/Playwright/API/Generated/Options/LocatorSelectOptionOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorSelectOptionOptions.cs @@ -54,13 +54,8 @@ public LocatorSelectOptionOptions(LocatorSelectOptionOptions clone) public bool? Force { get; set; } /// - /// **DEPRECATED** This option will default to true in the future. - /// - /// Actions that initiate navigations are waiting for these navigations to happen and - /// for pages to start loading. You can opt out of waiting via setting this flag. You - /// would only need this option in the exceptional cases such as navigating to inaccessible - /// pages. Defaults to false. - /// + /// **DEPRECATED** This option has no effect. + /// This option has no effect. /// [JsonPropertyName("noWaitAfter")] [System.Obsolete] diff --git a/src/Playwright/API/Generated/Options/PageRouteFromHAROptions.cs b/src/Playwright/API/Generated/Options/PageRouteFromHAROptions.cs index 3fbfa752b8..1284819464 100644 --- a/src/Playwright/API/Generated/Options/PageRouteFromHAROptions.cs +++ b/src/Playwright/API/Generated/Options/PageRouteFromHAROptions.cs @@ -83,7 +83,7 @@ public PageRouteFromHAROptions(PageRouteFromHAROptions clone) /// /// When set to minimal, only record information necessary for routing from HAR. /// This omits sizes, timing, page, cookies, security and other types of HAR information - /// that are not used when replaying from HAR. Defaults to full. + /// that are not used when replaying from HAR. Defaults to minimal. /// /// [JsonPropertyName("updateMode")] diff --git a/src/Playwright/API/Generated/Options/PageSelectOptionOptions.cs b/src/Playwright/API/Generated/Options/PageSelectOptionOptions.cs index b87618222d..a89e99bb20 100644 --- a/src/Playwright/API/Generated/Options/PageSelectOptionOptions.cs +++ b/src/Playwright/API/Generated/Options/PageSelectOptionOptions.cs @@ -55,13 +55,8 @@ public PageSelectOptionOptions(PageSelectOptionOptions clone) public bool? Force { get; set; } /// - /// **DEPRECATED** This option will default to true in the future. - /// - /// Actions that initiate navigations are waiting for these navigations to happen and - /// for pages to start loading. You can opt out of waiting via setting this flag. You - /// would only need this option in the exceptional cases such as navigating to inaccessible - /// pages. Defaults to false. - /// + /// **DEPRECATED** This option has no effect. + /// This option has no effect. /// [JsonPropertyName("noWaitAfter")] [System.Obsolete] diff --git a/src/Playwright/API/Generated/Types/ClientCertificate.cs b/src/Playwright/API/Generated/Types/ClientCertificate.cs index da2b2a1656..6a3edb874a 100644 --- a/src/Playwright/API/Generated/Types/ClientCertificate.cs +++ b/src/Playwright/API/Generated/Types/ClientCertificate.cs @@ -45,14 +45,26 @@ public partial class ClientCertificate [JsonPropertyName("certPath")] public string? CertPath { get; set; } + /// Direct value of the certificate in PEM format. + [JsonPropertyName("cert")] + public byte[]? Cert { get; set; } + /// Path to the file with the private key in PEM format. [JsonPropertyName("keyPath")] public string? KeyPath { get; set; } + /// Direct value of the private key in PEM format. + [JsonPropertyName("key")] + public byte[]? Key { get; set; } + /// Path to the PFX or PKCS12 encoded private key and certificate chain. [JsonPropertyName("pfxPath")] public string? PfxPath { get; set; } + /// Direct value of the PFX or PKCS12 encoded private key and certificate chain. + [JsonPropertyName("pfx")] + public byte[]? Pfx { get; set; } + /// Passphrase for the private key (PEM or PFX). [JsonPropertyName("passphrase")] public string? Passphrase { get; set; } diff --git a/src/Playwright/Core/APIRequestContext.cs b/src/Playwright/Core/APIRequestContext.cs index 90b11cc49b..3f3aef0d8f 100644 --- a/src/Playwright/Core/APIRequestContext.cs +++ b/src/Playwright/Core/APIRequestContext.cs @@ -29,6 +29,7 @@ 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; @@ -114,12 +115,6 @@ public async Task FetchAsync(string url, APIRequestContextOptions { throw new PlaywrightException("Only one of 'data', 'form' or 'multipart' can be specified"); } - - var queryParams = new Dictionary(); - if (options.Params != null) - { - queryParams = options.Params.ToDictionary(x => x.Key, x => x.Value.ToString()); - } byte[] postData = null; object jsonData = null; string dataString = !string.IsNullOrEmpty(options.Data) ? options.Data : options.DataString; @@ -152,7 +147,7 @@ public async Task FetchAsync(string url, APIRequestContextOptions ["maxRedirects"] = options?.MaxRedirects, ["maxRetries"] = options?.MaxRetries, ["timeout"] = options.Timeout, - ["params"] = queryParams?.ToProtocol(), + ["params"] = QueryParamsToProtocol(options.Params, options.ParamsString), ["headers"] = options.Headers?.ToProtocol(), ["jsonData"] = jsonData, ["postData"] = postData != null ? Convert.ToBase64String(postData) : null, @@ -164,6 +159,28 @@ 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/Browser.cs b/src/Playwright/Core/Browser.cs index 30468c0640..783162a114 100644 --- a/src/Playwright/Core/Browser.cs +++ b/src/Playwright/Core/Browser.cs @@ -329,12 +329,25 @@ internal static Dictionary[] ToClientCertificatesProtocol(IEnume { ["origin"] = clientCertificate.Origin, ["passphrase"] = clientCertificate.Passphrase, - ["cert"] = clientCertificate.CertPath != null ? Convert.ToBase64String(File.ReadAllBytes(clientCertificate.CertPath)) : null, - ["key"] = clientCertificate.KeyPath != null ? Convert.ToBase64String(File.ReadAllBytes(clientCertificate.KeyPath)) : null, - ["pfx"] = clientCertificate.PfxPath != null ? Convert.ToBase64String(File.ReadAllBytes(clientCertificate.PfxPath)) : null, + ["cert"] = ReadClientCertificateFile(clientCertificate.CertPath, clientCertificate.Cert), + ["key"] = ReadClientCertificateFile(clientCertificate.KeyPath, clientCertificate.Key), + ["pfx"] = ReadClientCertificateFile(clientCertificate.PfxPath, clientCertificate.Pfx), } .Where(kv => kv.Value != null) .ToDictionary(kv => kv.Key, kv => kv.Value)) .ToArray(); } + + private static string ReadClientCertificateFile(string path, byte[] value) + { + if (value != null) + { + return Convert.ToBase64String(value); + } + if (path != null) + { + return Convert.ToBase64String(File.ReadAllBytes(path)); + } + return null; + } } diff --git a/src/Playwright/Core/ElementHandle.cs b/src/Playwright/Core/ElementHandle.cs index b7aaa40e30..6fcf3e3128 100644 --- a/src/Playwright/Core/ElementHandle.cs +++ b/src/Playwright/Core/ElementHandle.cs @@ -346,7 +346,6 @@ private async Task> _selectOptionAsync(IEnumerable { ["options"] = values, - ["noWaitAfter"] = noWaitAfter, ["force"] = force, ["timeout"] = timeout, }).ConfigureAwait(false))?.GetProperty("values").ToObject(); @@ -357,7 +356,6 @@ private async Task> _selectOptionAsync(IEnumerable { ["elements"] = values, - ["noWaitAfter"] = noWaitAfter, ["force"] = force, ["timeout"] = timeout, }).ConfigureAwait(false))?.GetProperty("values").ToObject(); diff --git a/src/Playwright/Core/Frame.cs b/src/Playwright/Core/Frame.cs index c4fc0aeaa4..5264b0c9bf 100644 --- a/src/Playwright/Core/Frame.cs +++ b/src/Playwright/Core/Frame.cs @@ -179,9 +179,6 @@ public async Task> SelectOptionAsync(string selector, IEnu { ["selector"] = selector, ["elements"] = values.Select(x => x as ElementHandle), -#pragma warning disable CS0612 // Type or member is obsolete - ["noWaitAfter"] = options?.NoWaitAfter, -#pragma warning restore CS0612 // Type or member is obsolete ["strict"] = options?.Strict, ["force"] = options?.Force, ["timeout"] = options?.Timeout, diff --git a/src/Playwright/Transport/Protocol/Generated/PlaywrightInitializer.cs b/src/Playwright/Transport/Protocol/Generated/PlaywrightInitializer.cs index 52d3ed90b2..c9424027fb 100644 --- a/src/Playwright/Transport/Protocol/Generated/PlaywrightInitializer.cs +++ b/src/Playwright/Transport/Protocol/Generated/PlaywrightInitializer.cs @@ -37,6 +37,12 @@ internal class PlaywrightInitializer [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; }