diff --git a/README.md b/README.md index 0f2db782..6d46f16f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 106.0.5249.21 | ✅ | ✅ | ✅ | +| Chromium 106.0.5249.30 | ✅ | ✅ | ✅ | | WebKit 16.0 | ✅ | ✅ | ✅ | | Firefox 104.0 | ✅ | ✅ | ✅ | diff --git a/src/Common/Version.props b/src/Common/Version.props index 950359eb..80333929 100644 --- a/src/Common/Version.props +++ b/src/Common/Version.props @@ -2,7 +2,7 @@ 1.25.0 $(AssemblyVersion)-next-2 - 1.26.0-alpha-1662365984000 + 1.26.0-beta-1662762342000 $(AssemblyVersion) $(AssemblyVersion) true diff --git a/src/Playwright.Tests/Assertions/LocatorAssertionsTests.cs b/src/Playwright.Tests/Assertions/LocatorAssertionsTests.cs index 6a14feef..28942654 100644 --- a/src/Playwright.Tests/Assertions/LocatorAssertionsTests.cs +++ b/src/Playwright.Tests/Assertions/LocatorAssertionsTests.cs @@ -51,86 +51,183 @@ namespace Microsoft.Playwright.Tests.Assertions StringAssert.Contains("LocatorAssertions.ToBeCheckedAsync with timeout 300ms", exception.Message); } - [PlaywrightTest("playwright-test/playwright.expect.spec.ts", "should support toBeEditable, toBeEnabled, toBeDisabled, toBeEmpty")] - public async Task ShouldSupportToBeEditableToBeEnabledToBeDisabledToBeEmpty() + [PlaywrightTest("playwright-test/playwright.expect.spec.ts", "should support toBeEditable")] + public async Task ShouldSupportToBeEditable() { { + // default await Page.SetContentAsync(""); - await Expect(Page.Locator("input")).ToBeEditableAsync(); + var locator = Page.Locator("input"); + await Expect(locator).ToBeEditableAsync(); } - { + // with not + await Page.SetContentAsync(""); + var locator = Page.Locator("input"); + await Expect(locator).Not.ToBeEditableAsync(); + } + { + // with editable:true + await Page.SetContentAsync(""); + var locator = Page.Locator("input"); + await Expect(locator).ToBeEditableAsync(new() { Editable = true }); + } + { + // with editable:false + await Page.SetContentAsync(""); + var locator = Page.Locator("input"); + await Expect(locator).ToBeEditableAsync(new() { Editable = false }); + } + { + // with not and editable:false + await Page.SetContentAsync(""); + var locator = Page.Locator("input"); + await Expect(locator).Not.ToBeEditableAsync(new() { Editable = false }); + } + } + + [PlaywrightTest("playwright-test/playwright.expect.spec.ts", "should support toBeEnabled")] + public async Task ShouldSupportToBeEnabled() + { + { + // default await Page.SetContentAsync(""); - await Expect(Page.Locator("button")).ToBeEnabledAsync(); - } - - { - await Page.SetContentAsync(""); - var exception = await PlaywrightAssert.ThrowsAsync(() => Expect(Page.Locator("button")).ToBeEnabledAsync(new() { Timeout = 500 })); - StringAssert.Contains("Locator expected to be enabled", exception.Message); - StringAssert.Contains("LocatorAssertions.ToBeEnabledAsync with timeout 500ms", exception.Message); - } - - { - await Page.SetContentAsync(""); var locator = Page.Locator("button"); - await locator.EvaluateAsync("e => setTimeout(() => { e.removeAttribute('disabled') }, 500);"); await Expect(locator).ToBeEnabledAsync(); } - { + // with enabled:true await Page.SetContentAsync(""); var locator = Page.Locator("button"); - await locator.EvaluateAsync("e => setTimeout(() => { e.setAttribute('disabled', '') }, 500);"); + await Expect(locator).ToBeEnabledAsync(new() { Enabled = true }); + } + { + // with enabled:false + await Page.SetContentAsync(""); + var locator = Page.Locator("button"); + await Expect(locator).ToBeEnabledAsync(new() { Enabled = false }); + } + { + // failed + await Page.SetContentAsync(""); + var locator = Page.Locator("button"); + var exception = await PlaywrightAssert.ThrowsAsync(() => Expect(locator).ToBeEnabledAsync(new() { Timeout = 1000 })); + StringAssert.Contains("selector resolved to ", exception.Message); + // extra checks + StringAssert.Contains("Locator expected to be enabled", exception.Message); + StringAssert.Contains("LocatorAssertions.ToBeEnabledAsync with timeout 1000ms", exception.Message); + } + { + // eventually + await Page.SetContentAsync(""); + var locator = Page.Locator("button"); + await locator.EvaluateAsync("e => setTimeout(() => e.removeAttribute('disabled'), 500);"); + await Expect(locator).ToBeEnabledAsync(); + } + { + // eventually with not + await Page.SetContentAsync(""); + var locator = Page.Locator("button"); + await locator.EvaluateAsync("e => setTimeout(() => e.setAttribute('disabled', ''), 500);"); await Expect(locator).Not.ToBeEnabledAsync(); } - { + // with not and enabled:false + await Page.SetContentAsync(""); + var locator = Page.Locator("button"); + await Expect(locator).Not.ToBeEnabledAsync(new() { Enabled = false }); + } + { + // toBeDisabled await Page.SetContentAsync(""); var locator = Page.Locator("button"); await Expect(locator).ToBeDisabledAsync(); } - - { - await Page.SetContentAsync(""); - var locator = Page.Locator("input"); - await Expect(locator).ToBeEmptyAsync(); - } - - { - await Page.SetContentAsync(""); - var locator = Page.Locator("input"); - await Expect(locator).Not.ToBeEmptyAsync(); - } } - [PlaywrightTest("playwright-test/playwright.expect.spec.ts", "should support toBeVisible, toBeHidden")] + [PlaywrightTest("playwright-test/playwright.expect.spec.ts", "should support toBeVisible")] public async Task ShouldSupportToBeVisibleToBeHidden() { { + // default await Page.SetContentAsync(""); var locator = Page.Locator("input"); await Expect(locator).ToBeVisibleAsync(); } { + // with not await Page.SetContentAsync(""); var locator = Page.Locator("button"); await Expect(locator).Not.ToBeVisibleAsync(); } { + // with visible:true + await Page.SetContentAsync(""); + var locator = Page.Locator("button"); + await Expect(locator).ToBeVisibleAsync(new() { Visible = true }); + } + { + // with visible:false + await Page.SetContentAsync(""); + var locator = Page.Locator("button"); + await Expect(locator).ToBeVisibleAsync(new() { Visible = false }); + } + { + // with not and visible:false + await Page.SetContentAsync(""); + var locator = Page.Locator("button"); + await Expect(locator).Not.ToBeVisibleAsync(new() { Visible = false }); + } + { + // eventually + await Page.SetContentAsync("
"); + var locator = Page.Locator("span"); + await Page.EvalOnSelectorAsync("div", "e => setTimeout(() => e.innerHTML = e.innerHTML + 'Hello', 500);"); + await Expect(locator).ToBeVisibleAsync(); + } + { + // eventually with not + await Page.SetContentAsync("
Hello
"); + var locator = Page.Locator("span"); + await Page.EvalOnSelectorAsync("span", "span => setTimeout(() => span.textContent = '', 500);"); + await Expect(locator).Not.ToBeVisibleAsync(); + } + { + // fail await Page.SetContentAsync(""); var locator = Page.Locator("button"); - await Expect(locator).ToBeHiddenAsync(); - } - { - await Page.SetContentAsync("
"); - var locator = Page.Locator("div"); - await Expect(locator).ToBeHiddenAsync(); + var exception = await PlaywrightAssert.ThrowsAsync(() => Expect(locator).ToBeVisibleAsync(new() { Timeout = 1000 })); + StringAssert.Contains("selector resolved to ", exception.Message); } { + // fail with not await Page.SetContentAsync(""); var locator = Page.Locator("input"); - await Expect(locator).Not.ToBeHiddenAsync(); + var exception = await PlaywrightAssert.ThrowsAsync(() => Expect(locator).Not.ToBeVisibleAsync(new() { Timeout = 1000 })); + StringAssert.Contains("selector resolved to ", exception.Message); + } + } + + [PlaywrightTest("playwright-test/playwright.expect.spec.ts", "should support ToBeEmpty")] + public async Task ShouldSupportToBeEmpty() + { + { + // toBeEmpty input + await Page.SetContentAsync(""); + var locator = Page.Locator("input"); + await Expect(locator).ToBeEmptyAsync(); + } + { + // not.toBeEmpty + await Page.SetContentAsync(""); + var locator = Page.Locator("input"); + await Expect(locator).Not.ToBeEmptyAsync(); + } + { + // toBeEmpty div + await Page.SetContentAsync("
"); + var locator = Page.Locator("div"); + await Expect(locator).ToBeEmptyAsync(); } } diff --git a/src/Playwright.Tests/GlobalFetchTests.cs b/src/Playwright.Tests/GlobalFetchTests.cs index 25669293..839fd298 100644 --- a/src/Playwright.Tests/GlobalFetchTests.cs +++ b/src/Playwright.Tests/GlobalFetchTests.cs @@ -253,5 +253,100 @@ namespace Microsoft.Playwright.Tests } await response.DisposeAsync(); } + + [PlaywrightTest("global-fetch.spec.ts", "should not fail on empty body with encoding")] + public async Task ShouldNotFailOnEmptyBodyWithEncoding() + { + var request = await Playwright.APIRequest.NewContextAsync(); + foreach (var method in new[] { "head", "put" }) + { + foreach (var encoding in new[] { "br", "gzip", "deflate" }) + { + Server.SetRoute("/empty.html", (ctx) => + { + ctx.Response.StatusCode = 200; + ctx.Response.Headers.Add("Content-Encoding", encoding); + ctx.Response.Headers.Add("Content-Type", "text/plain"); + return Task.CompletedTask; + }); + var response = await request.NameToMethod(method)(Server.EmptyPage, null); + Assert.AreEqual(200, response.Status); + Assert.AreEqual(0, (await response.BodyAsync()).Length); + } + } + await request.DisposeAsync(); + } + + [PlaywrightTest("global-fetch.spec.ts", "should return body for failing requests")] + public async Task ShouldReturnBodyForFailingRequests() + { + var request = await Playwright.APIRequest.NewContextAsync(); + foreach (var method in new[] { "head", "put", "trace" }) + { + Server.SetRoute("/empty.html", async (ctx) => + { + ctx.Response.StatusCode = 404; + ctx.Response.Headers.Add("Content-Length", "10"); + ctx.Response.Headers.Add("Content-Type", "text/plain"); + await ctx.Response.WriteAsync("Not found."); + }); + var response = await request.FetchAsync(Server.EmptyPage, new() { Method = method }); + Assert.AreEqual(404, response.Status); + // HEAD response returns empty body in node http module. + Assert.AreEqual(method == "head" ? "" : "Not found.", await response.TextAsync()); + } + await request.DisposeAsync(); + } + + [PlaywrightTest("global-fetch.spec.ts", "should throw an error when maxRedirects is exceeded")] + public async Task ShouldThrowAnErrorWhenMaxRedirectsIsExceeded() + { + Server.SetRedirect("/a/redirect1", "/b/c/redirect2"); + Server.SetRedirect("/b/c/redirect2", "/b/c/redirect3"); + Server.SetRedirect("/b/c/redirect3", "/b/c/redirect4"); + Server.SetRedirect("/b/c/redirect4", "/simple.json"); + + var request = await Playwright.APIRequest.NewContextAsync(); + foreach (var method in new[] { "GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH" }) + { + foreach (var maxRedirects in new[] { 1, 2, 3 }) + { + var exception = await PlaywrightAssert.ThrowsAsync(() => request.FetchAsync($"{Server.Prefix}/a/redirect1", new() { Method = method, MaxRedirects = maxRedirects })); + StringAssert.Contains("Max redirect count exceeded", exception.Message); + } + } + await request.DisposeAsync(); + } + + [PlaywrightTest("global-fetch.spec.ts", "should not follow redirects when maxRedirects is set to 0")] + public async Task ShouldNotFollowRedirectsWhenMaxRedirectsIsSetTo0() + { + Server.SetRedirect("/a/redirect1", "/b/c/redirect2"); + Server.SetRedirect("/b/c/redirect2", "/simple.json"); + + var request = await Playwright.APIRequest.NewContextAsync(); + foreach (var method in new[] { "GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH" }) + { + var response = await request.FetchAsync($"{Server.Prefix}/a/redirect1", new() { Method = method, MaxRedirects = 0 }); + Assert.AreEqual("/b/c/redirect2", response.Headers["location"]); + Assert.AreEqual(302, response.Status); + } + await request.DisposeAsync(); + } + + [PlaywrightTest("global-fetch.spec.ts", "should throw an error when maxRedirects is less than 0")] + public async Task ShouldThrowAnErrorWhenMaxRedirectsIsLessThan0() + { + Server.SetRedirect("/a/redirect1", "/b/c/redirect2"); + Server.SetRedirect("/b/c/redirect2", "/simple.json"); + + var request = await Playwright.APIRequest.NewContextAsync(); + foreach (var method in new[] { "GET", "PUT", "POST", "OPTIONS", "HEAD", "PATCH" }) + { + var exception = await PlaywrightAssert.ThrowsAsync(() => request.FetchAsync($"{Server.Prefix}/a/redirect1", new() { Method = method, MaxRedirects = -1 })); + StringAssert.Contains("'maxRedirects' should be greater than or equal to '0'", exception.Message); + } + await request.DisposeAsync(); + } } } diff --git a/src/Playwright/API/Generated/IBrowserType.cs b/src/Playwright/API/Generated/IBrowserType.cs index c01ff77d..b8b37ccd 100644 --- a/src/Playwright/API/Generated/IBrowserType.cs +++ b/src/Playwright/API/Generated/IBrowserType.cs @@ -84,6 +84,11 @@ namespace Microsoft.Playwright /// DevTools Protocol. /// /// The default browser context is accessible via . + /// + /// var browser = await playwright.Chromium.ConnectOverCDPAsync("http://localhost:9222");
+ /// var defaultContext = browser.Contexts[0];
+ /// var page = defaultContext.Pages[0]; + ///
/// /// /// diff --git a/src/Playwright/API/Generated/ILocatorAssertions.cs b/src/Playwright/API/Generated/ILocatorAssertions.cs index 4bec5b56..bcb6e3bd 100644 --- a/src/Playwright/API/Generated/ILocatorAssertions.cs +++ b/src/Playwright/API/Generated/ILocatorAssertions.cs @@ -53,9 +53,10 @@ namespace Microsoft.Playwright ///
/// namespace PlaywrightTests;
///
+ /// [TestFixture]
/// public class ExampleTests : PageTest
/// {
- /// [Test]
+ /// [PlaywrightTest]
/// public async Task StatusBecomesSubmitted()
/// {
/// // ..
diff --git a/src/Playwright/API/Generated/IPageAssertions.cs b/src/Playwright/API/Generated/IPageAssertions.cs index 71436933..6ae50c10 100644 --- a/src/Playwright/API/Generated/IPageAssertions.cs +++ b/src/Playwright/API/Generated/IPageAssertions.cs @@ -53,9 +53,10 @@ namespace Microsoft.Playwright ///
/// namespace PlaywrightTests;
///
+ /// [TestFixture]
/// public class ExampleTests : PageTest
/// {
- /// [Test]
+ /// [PlaywrightTest]
/// public async Task NavigatetoLoginPage()
/// {
/// // ..
diff --git a/src/Playwright/API/Generated/IPlaywrightAssertions.cs b/src/Playwright/API/Generated/IPlaywrightAssertions.cs index 55772481..fb964eda 100644 --- a/src/Playwright/API/Generated/IPlaywrightAssertions.cs +++ b/src/Playwright/API/Generated/IPlaywrightAssertions.cs @@ -52,9 +52,10 @@ namespace Microsoft.Playwright ///
/// namespace PlaywrightTests;
///
+ /// [TestFixture]
/// public class ExampleTests : PageTest
/// {
- /// [Test]
+ /// [PlaywrightTest]
/// public async Task StatusBecomesSubmitted()
/// {
/// await Page.Locator("#submit-button").ClickAsync();
diff --git a/src/Playwright/API/Generated/Options/APIRequestContextOptions.cs b/src/Playwright/API/Generated/Options/APIRequestContextOptions.cs index 665b5a62..00f4906b 100644 --- a/src/Playwright/API/Generated/Options/APIRequestContextOptions.cs +++ b/src/Playwright/API/Generated/Options/APIRequestContextOptions.cs @@ -57,6 +57,7 @@ namespace Microsoft.Playwright Form = clone.Form; Headers = clone.Headers; IgnoreHTTPSErrors = clone.IgnoreHTTPSErrors; + MaxRedirects = clone.MaxRedirects; Method = clone.Method; Multipart = clone.Multipart; Params = clone.Params; @@ -125,6 +126,16 @@ namespace Microsoft.Playwright [JsonPropertyName("ignoreHTTPSErrors")] public bool? IgnoreHTTPSErrors { get; set; } + /// + /// + /// Maximum number of request redirects that will be followed automatically. An error + /// will be thrown if the number is exceeded. Defaults to 20. Pass 0 to + /// not follow redirects. + /// + /// + [JsonPropertyName("maxRedirects")] + public int? MaxRedirects { get; set; } + /// /// /// If set changes the fetch method (e.g. PUT diff --git a/src/Playwright/API/Generated/Options/LocatorAssertionsToBeEditableOptions.cs b/src/Playwright/API/Generated/Options/LocatorAssertionsToBeEditableOptions.cs index 5d42b878..418540f8 100644 --- a/src/Playwright/API/Generated/Options/LocatorAssertionsToBeEditableOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorAssertionsToBeEditableOptions.cs @@ -50,9 +50,13 @@ namespace Microsoft.Playwright return; } + Editable = clone.Editable; Timeout = clone.Timeout; } + [JsonPropertyName("editable")] + public bool? Editable { get; set; } + /// Time to retry the assertion for. [JsonPropertyName("timeout")] public float? Timeout { get; set; } diff --git a/src/Playwright/API/Generated/Options/LocatorAssertionsToBeEnabledOptions.cs b/src/Playwright/API/Generated/Options/LocatorAssertionsToBeEnabledOptions.cs index fc3518d4..7b9e22d0 100644 --- a/src/Playwright/API/Generated/Options/LocatorAssertionsToBeEnabledOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorAssertionsToBeEnabledOptions.cs @@ -50,9 +50,13 @@ namespace Microsoft.Playwright return; } + Enabled = clone.Enabled; Timeout = clone.Timeout; } + [JsonPropertyName("enabled")] + public bool? Enabled { get; set; } + /// Time to retry the assertion for. [JsonPropertyName("timeout")] public float? Timeout { get; set; } diff --git a/src/Playwright/API/Generated/Options/LocatorAssertionsToBeVisibleOptions.cs b/src/Playwright/API/Generated/Options/LocatorAssertionsToBeVisibleOptions.cs index 62ab20c5..b01c8451 100644 --- a/src/Playwright/API/Generated/Options/LocatorAssertionsToBeVisibleOptions.cs +++ b/src/Playwright/API/Generated/Options/LocatorAssertionsToBeVisibleOptions.cs @@ -51,11 +51,15 @@ namespace Microsoft.Playwright } Timeout = clone.Timeout; + Visible = clone.Visible; } /// Time to retry the assertion for. [JsonPropertyName("timeout")] public float? Timeout { get; set; } + + [JsonPropertyName("visible")] + public bool? Visible { get; set; } } } diff --git a/src/Playwright/Core/APIRequestContext.cs b/src/Playwright/Core/APIRequestContext.cs index 17087d02..b7e07003 100644 --- a/src/Playwright/Core/APIRequestContext.cs +++ b/src/Playwright/Core/APIRequestContext.cs @@ -75,6 +75,16 @@ namespace Microsoft.Playwright.Core public async Task FetchAsync(string url, APIRequestContextOptions options = null) { options ??= new APIRequestContextOptions(); + + if (options.MaxRedirects != null && options.MaxRedirects < 0) + { + throw new PlaywrightException("'maxRedirects' should be greater than or equal to '0'"); + } + if (new[] { options.DataByte, options.DataObject, options.DataString, options.Form, options.Multipart }.Count(x => x != null) > 1) + { + throw new PlaywrightException("Only one of 'data', 'form' or 'multipart' can be specified"); + } + var queryParams = new Dictionary(); if (options.Params != null) { @@ -113,7 +123,8 @@ namespace Microsoft.Playwright.Core (FormData)options.Multipart, options.Timeout, options?.FailOnStatusCode, - options?.IgnoreHTTPSErrors).ConfigureAwait(false); + options?.IgnoreHTTPSErrors, + options?.MaxRedirects).ConfigureAwait(false); } private bool IsJsonContentType(IDictionary headers) diff --git a/src/Playwright/Core/LocatorAssertions.cs b/src/Playwright/Core/LocatorAssertions.cs index 815057a3..900de63e 100644 --- a/src/Playwright/Core/LocatorAssertions.cs +++ b/src/Playwright/Core/LocatorAssertions.cs @@ -46,17 +46,32 @@ namespace Microsoft.Playwright.Core public Task ToBeDisabledAsync(LocatorAssertionsToBeDisabledOptions options = null) => ExpectTrueAsync("to.be.disabled", "Locator expected to be disabled", ConvertToFrameExpectOptions(options)); - public Task ToBeEditableAsync(LocatorAssertionsToBeEditableOptions options = null) => ExpectTrueAsync("to.be.editable", "Locator expected to be editable", ConvertToFrameExpectOptions(options)); + public Task ToBeEditableAsync(LocatorAssertionsToBeEditableOptions options = null) + { + var editable = options == null || options.Editable == null || options.Editable == true; + var editableString = editable ? "editable" : "read-only"; + return ExpectTrueAsync(editable ? "to.be.editable" : "to.be.readonly", $"Locator expected to be {editableString}", ConvertToFrameExpectOptions(options)); + } public Task ToBeEmptyAsync(LocatorAssertionsToBeEmptyOptions options = null) => ExpectTrueAsync("to.be.empty", "Locator expected to be empty", ConvertToFrameExpectOptions(options)); - public Task ToBeEnabledAsync(LocatorAssertionsToBeEnabledOptions options = null) => ExpectTrueAsync("to.be.enabled", "Locator expected to be enabled", ConvertToFrameExpectOptions(options)); + public Task ToBeEnabledAsync(LocatorAssertionsToBeEnabledOptions options = null) + { + var enabled = options == null || options.Enabled == null || options.Enabled == true; + var enabledString = enabled ? "enabled" : "disabled"; + return ExpectTrueAsync(enabled ? "to.be.enabled" : "to.be.disabled", $"Locator expected to be {enabledString}", ConvertToFrameExpectOptions(options)); + } public Task ToBeFocusedAsync(LocatorAssertionsToBeFocusedOptions options = null) => ExpectTrueAsync("to.be.focused", "Locator expected to be focused", ConvertToFrameExpectOptions(options)); public Task ToBeHiddenAsync(LocatorAssertionsToBeHiddenOptions options = null) => ExpectTrueAsync("to.be.hidden", "Locator expected to be hidden", ConvertToFrameExpectOptions(options)); - public Task ToBeVisibleAsync(LocatorAssertionsToBeVisibleOptions options = null) => ExpectTrueAsync("to.be.visible", "Locator expected to be visible", ConvertToFrameExpectOptions(options)); + public Task ToBeVisibleAsync(LocatorAssertionsToBeVisibleOptions options = null) + { + var visible = options == null || options.Visible == null || options.Visible == true; + var visibleString = visible ? "visible" : "hidden"; + return ExpectTrueAsync(visible ? "to.be.visible" : "to.be.hidden", $"Locator expected to be {visibleString}", ConvertToFrameExpectOptions(options)); + } private Task ExpectTrueAsync(string expression, string message, FrameExpectOptions options) { diff --git a/src/Playwright/Transport/Channels/APIRequestContextChannel.cs b/src/Playwright/Transport/Channels/APIRequestContextChannel.cs index 35c4df59..1f272b72 100644 --- a/src/Playwright/Transport/Channels/APIRequestContextChannel.cs +++ b/src/Playwright/Transport/Channels/APIRequestContextChannel.cs @@ -50,7 +50,8 @@ namespace Microsoft.Playwright.Transport.Channels FormData multipartData, float? timeout, bool? failOnStatusCode, - bool? ignoreHTTPSErrors) + bool? ignoreHTTPSErrors, + int? maxRedirects) { var message = new Dictionary { @@ -58,6 +59,7 @@ namespace Microsoft.Playwright.Transport.Channels ["method"] = method, ["failOnStatusCode"] = failOnStatusCode, ["ignoreHTTPSErrors"] = ignoreHTTPSErrors, + ["maxRedirects"] = maxRedirects, ["timeout"] = timeout, ["params"] = parameters?.ToProtocol(), ["headers"] = headers?.ToProtocol(),