diff --git a/README.md b/README.md index 4edfeb24..8d54d71b 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ | | Linux | macOS | Windows | | :--- | :---: | :---: | :---: | -| Chromium 115.0.5790.75 | ✅ | ✅ | ✅ | +| Chromium 116.0.5845.62 | ✅ | ✅ | ✅ | | WebKit 17.0 | ✅ | ✅ | ✅ | | Firefox 115.0 | ✅ | ✅ | ✅ | diff --git a/src/Common/Version.props b/src/Common/Version.props index d23ed508..b85afaa1 100644 --- a/src/Common/Version.props +++ b/src/Common/Version.props @@ -2,7 +2,7 @@ 1.36.0 $(AssemblyVersion) - 1.36.0 + 1.37.0-alpha-aug-8-2023 $(AssemblyVersion) $(AssemblyVersion) true diff --git a/src/Playwright.Tests/BrowserContextFetchTests.cs b/src/Playwright.Tests/BrowserContextFetchTests.cs index 4fb1e8a4..ff5a7f37 100644 --- a/src/Playwright.Tests/BrowserContextFetchTests.cs +++ b/src/Playwright.Tests/BrowserContextFetchTests.cs @@ -66,7 +66,7 @@ public class BrowserContextFetchTests : PageTestEx return Task.CompletedTask; }); var error = await PlaywrightAssert.ThrowsAsync(async () => await Context.APIRequest.GetAsync(Server.Prefix + "/test")); - StringAssert.Contains("socket hang up", error.Message); + Assert.True(error.Message.Contains("read ECONNRESET") || error.Message.Contains("socket hang up")); } [PlaywrightTest("browsercontext-fetch.spec.ts", "should throw on network error after redirect")] @@ -79,7 +79,7 @@ public class BrowserContextFetchTests : PageTestEx return Task.CompletedTask; }); var error = await PlaywrightAssert.ThrowsAsync(async () => await Context.APIRequest.GetAsync(Server.Prefix + "/redirect")); - StringAssert.Contains("socket hang up", error.Message); + Assert.True(error.Message.Contains("read ECONNRESET") || error.Message.Contains("socket hang up")); } [PlaywrightTest("browsercontext-fetch.spec.ts", "should add session cookies to request")] diff --git a/src/Playwright.Tests/FrameGoToTests.cs b/src/Playwright.Tests/FrameGoToTests.cs index e2cc5162..40328c24 100644 --- a/src/Playwright.Tests/FrameGoToTests.cs +++ b/src/Playwright.Tests/FrameGoToTests.cs @@ -51,7 +51,14 @@ public class FrameGoToTests : PageTestEx await Server.WaitForRequest("/one-style.css"); await Page.EvalOnSelectorAsync("iframe", "frame => frame.remove()"); var exception = await PlaywrightAssert.ThrowsAsync(() => navigationTask); - Assert.True(exception.Message.Contains("frame was detached") || exception.Message.Contains("net::ERR_ABORTED")); + if (BrowserName == "chromium") + { + Assert.True(exception.Message.Contains("net::ERR_ABORTED") || exception.Message.ToLowerInvariant().Contains("frame was detached")); + } + else + { + Assert.True(exception.Message.ToLowerInvariant().Contains("frame was detached")); + } } [PlaywrightTest("frame-goto.spec.ts", "should continue after client redirect")] diff --git a/src/Playwright.Tests/InterceptionTests.cs b/src/Playwright.Tests/InterceptionTests.cs index 05c4ad11..5c2f1f4f 100644 --- a/src/Playwright.Tests/InterceptionTests.cs +++ b/src/Playwright.Tests/InterceptionTests.cs @@ -50,12 +50,14 @@ public class GlobTests : PageTestEx Assert.That("http://localhost:3000/signin-oidc/foo", Does.Not.Match(StringExtensions.GlobToRegex("http://localhost:3000/signin-oidc*"))); Assert.That("http://localhost:3000/signin-oidcnice", Does.Match(StringExtensions.GlobToRegex("http://localhost:3000/signin-oidc*"))); + Assert.That("http://mydomain:8080/blah/blah/three-columns/settings.html?id=settings-e3c58efe-02e9-44b0-97ac-dd138100cf7c&blah", Does.Match(StringExtensions.GlobToRegex("**/three-columns/settings.html?**id=[a-z]**"))); + Assert.AreEqual("^\\?$", StringExtensions.GlobToRegex("\\?")); Assert.AreEqual("^\\\\$", StringExtensions.GlobToRegex("\\")); Assert.AreEqual("^\\\\$", StringExtensions.GlobToRegex("\\\\")); Assert.AreEqual("^\\[$", StringExtensions.GlobToRegex("\\[")); - Assert.AreEqual("^\\[$", StringExtensions.GlobToRegex("[")); - Assert.AreEqual("^\\$\\^\\+\\.\\*\\(\\)\\|\\?\\{\\}\\[\\]$", StringExtensions.GlobToRegex("$^+.\\*()|\\?\\{\\}[]")); + Assert.AreEqual("^[a-z]$", StringExtensions.GlobToRegex("[a-z]")); + Assert.AreEqual(@"^\$\^\+\.\*\(\)\|\?\{\}\[\]$", StringExtensions.GlobToRegex("$^+.\\*()|\\?\\{\\}\\[\\]")); } [PlaywrightTest("interception.spec.ts", "should work with ignoreHTTPSErrors")] diff --git a/src/Playwright.Tests/Locator/LocatorQueryTests.cs b/src/Playwright.Tests/Locator/LocatorQueryTests.cs index 9b92832b..fb47f4e8 100644 --- a/src/Playwright.Tests/Locator/LocatorQueryTests.cs +++ b/src/Playwright.Tests/Locator/LocatorQueryTests.cs @@ -234,6 +234,23 @@ public class LocatorQueryTests : PageTestEx await Expect(Page.Locator("span").Or(Page.Locator("article"))).ToHaveTextAsync("world"); } + [PlaywrightTest("locator-query.spec.ts", "should support locator.locator with and/or")] + public async Task ShouldSupportLocatorLocatorWithAndOr() + { + await Page.SetContentAsync(@" +
one two
+ four + + "); + + await Expect(Page.Locator("div").Locator(Page.Locator("button"))).ToHaveTextAsync(new[] { "three" }); + await Expect(Page.Locator("div").Locator(Page.Locator("button").Or(Page.Locator("span")))).ToHaveTextAsync(new[] { "two", "three" }); + await Expect(Page.Locator("button").Or(Page.Locator("span"))).ToHaveTextAsync(new[] { "two", "three", "four", "five" }); + + await Expect(Page.Locator("div").Locator(Page.Locator("button").And(Page.GetByRole(AriaRole.Button)))).ToHaveTextAsync(new[] { "three" }); + await Expect(Page.Locator("button").And(Page.GetByRole(AriaRole.Button))).ToHaveTextAsync(new[] { "three", "five" }); + } + [PlaywrightTest("locator-query.spec.ts", "should enforce same frame for has:locator'")] public async Task ShouldEnforceSameFrameForHasLocator() { diff --git a/src/Playwright/API/Generated/IBrowserContext.cs b/src/Playwright/API/Generated/IBrowserContext.cs index 69b85a1a..07daf72d 100644 --- a/src/Playwright/API/Generated/IBrowserContext.cs +++ b/src/Playwright/API/Generated/IBrowserContext.cs @@ -625,7 +625,7 @@ public partial interface IBrowserContext /// /// /// If specified the network requests that are made in the context will be served from - /// the HAR file. Read more about Replaying + /// the HAR file. Read more about Replaying /// from HAR. /// /// diff --git a/src/Playwright/API/Generated/IFrame.cs b/src/Playwright/API/Generated/IFrame.cs index 3bd1428b..b9b12617 100644 --- a/src/Playwright/API/Generated/IFrame.cs +++ b/src/Playwright/API/Generated/IFrame.cs @@ -1424,6 +1424,12 @@ public partial interface IFrame /// Call options Task SetCheckedAsync(string selector, bool checkedState, FrameSetCheckedOptions? options = default); + /// + /// + /// This method internally calls document.write(), + /// inheriting all its specific characteristics and behaviors. + /// + /// /// HTML markup to assign to the page. /// Call options Task SetContentAsync(string html, FrameSetContentOptions? options = default); diff --git a/src/Playwright/API/Generated/ILocator.cs b/src/Playwright/API/Generated/ILocator.cs index 8fad965d..16b6b1c4 100644 --- a/src/Playwright/API/Generated/ILocator.cs +++ b/src/Playwright/API/Generated/ILocator.cs @@ -35,7 +35,7 @@ namespace Microsoft.Playwright; /// /// Locators are the central piece of Playwright's auto-waiting and retry-ability. In /// a nutshell, locators represent a way to find element(s) on the page at any moment. -/// Locator can be created with the method. +/// A locator can be created with the method. /// /// Learn more about locators. /// @@ -43,8 +43,8 @@ public partial interface ILocator { /// /// - /// When locator points to a list of elements, returns array of locators, pointing to - /// respective elements. + /// When the locator points to a list of elements, this returns an array of locators, + /// pointing to their respective elements. /// /// **Usage** /// @@ -1544,6 +1544,12 @@ public partial interface ILocator /// await element.PressAsync("Enter"); /// /// + /// + /// + /// In most cases, you should use instead. You only + /// need to type characters if there is special keyboard handling on the page. + /// + /// /// A text to type into a focused element. /// Call options Task TypeAsync(string text, LocatorTypeOptions? options = default); diff --git a/src/Playwright/API/Generated/IPage.cs b/src/Playwright/API/Generated/IPage.cs index 0c85dc7a..13101828 100644 --- a/src/Playwright/API/Generated/IPage.cs +++ b/src/Playwright/API/Generated/IPage.cs @@ -1879,7 +1879,7 @@ public partial interface IPage /// /// /// If specified the network requests that are made in the page will be served from - /// the HAR file. Read more about Replaying + /// the HAR file. Read more about Replaying /// from HAR. /// /// @@ -2217,6 +2217,12 @@ public partial interface IPage /// Call options Task SetCheckedAsync(string selector, bool checkedState, PageSetCheckedOptions? options = default); + /// + /// + /// This method internally calls document.write(), + /// inheriting all its specific characteristics and behaviors. + /// + /// /// HTML markup to assign to the page. /// Call options Task SetContentAsync(string html, PageSetContentOptions? options = default); diff --git a/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs b/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs index cf438dce..85d74f18 100644 --- a/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserNewContextOptions.cs @@ -186,7 +186,7 @@ public class BrowserNewContextOptions /// /// Whether the meta viewport tag is taken into account and touch events are /// enabled. isMobile is a part of device, so you don't actually need to set it manually. - /// Defaults to false and is not supported in Firefox. Learn more about mobile + /// Defaults to false and is not supported in Firefox. Learn more about mobile /// emulation. /// /// diff --git a/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs b/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs index ef949b49..cded6a39 100644 --- a/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserNewPageOptions.cs @@ -186,7 +186,7 @@ public class BrowserNewPageOptions /// /// Whether the meta viewport tag is taken into account and touch events are /// enabled. isMobile is a part of device, so you don't actually need to set it manually. - /// Defaults to false and is not supported in Firefox. Learn more about mobile + /// Defaults to false and is not supported in Firefox. Learn more about mobile /// emulation. /// /// diff --git a/src/Playwright/API/Generated/Options/BrowserTypeConnectOptions.cs b/src/Playwright/API/Generated/Options/BrowserTypeConnectOptions.cs index 807751d2..dda6a8c1 100644 --- a/src/Playwright/API/Generated/Options/BrowserTypeConnectOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserTypeConnectOptions.cs @@ -40,11 +40,42 @@ public class BrowserTypeConnectOptions return; } + ExposeNetwork = clone.ExposeNetwork; Headers = clone.Headers; SlowMo = clone.SlowMo; Timeout = clone.Timeout; } + /// + /// + /// This option exposes network available on the connecting client to the browser being + /// connected to. Consists of a list of rules separated by comma. + /// + /// Available rules: + /// + /// + /// Hostname pattern, for example: example.com, *.org:99, x.*.y.com, + /// *foo.org. + /// + /// IP literal, for example: 127.0.0.1, 0.0.0.0:99, [::1], [0:0::1]:99. + /// + /// <loopback> that matches local loopback interfaces: localhost, + /// *.localhost, 127.0.0.1, [::1]. + /// + /// + /// Some common examples: + /// + /// "*" to expose all network. + /// "<loopback>" to expose localhost network. + /// + /// "*.test.internal-domain,*.staging.internal-domain,<loopback>" to expose + /// test/staging deployments and localhost. + /// + /// + /// + [JsonPropertyName("exposeNetwork")] + public string? ExposeNetwork { get; set; } + /// Additional HTTP headers to be sent with web socket connect request. Optional. [JsonPropertyName("headers")] public IEnumerable>? Headers { get; set; } diff --git a/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs b/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs index 364e49c7..23413bce 100644 --- a/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs +++ b/src/Playwright/API/Generated/Options/BrowserTypeLaunchPersistentContextOptions.cs @@ -300,7 +300,7 @@ public class BrowserTypeLaunchPersistentContextOptions /// /// Whether the meta viewport tag is taken into account and touch events are /// enabled. isMobile is a part of device, so you don't actually need to set it manually. - /// Defaults to false and is not supported in Firefox. Learn more about mobile + /// Defaults to false and is not supported in Firefox. Learn more about mobile /// emulation. /// /// diff --git a/src/Playwright/Core/BrowserType.cs b/src/Playwright/Core/BrowserType.cs index fd1ac10b..b42487f1 100644 --- a/src/Playwright/Core/BrowserType.cs +++ b/src/Playwright/Core/BrowserType.cs @@ -166,7 +166,7 @@ internal class BrowserType : ChannelOwnerBase, IChannelOwner, IBrow new KeyValuePair("x-playwright-browser", Name), }.ToDictionary(pair => pair.Key, pair => pair.Value); var localUtils = _channel.Connection.LocalUtils; - var pipe = await localUtils.ConnectAsync(wsEndpoint: wsEndpoint, headers: headers, slowMo: options.SlowMo, timeout: options.Timeout).ConfigureAwait(false); + var pipe = await localUtils.ConnectAsync(wsEndpoint: wsEndpoint, headers: headers, slowMo: options.SlowMo, timeout: options.Timeout, exposeNetwork: options.ExposeNetwork).ConfigureAwait(false); void ClosePipe() { diff --git a/src/Playwright/Core/LocalUtils.cs b/src/Playwright/Core/LocalUtils.cs index 56e7e5db..6a1f9f4a 100644 --- a/src/Playwright/Core/LocalUtils.cs +++ b/src/Playwright/Core/LocalUtils.cs @@ -65,8 +65,8 @@ internal class LocalUtils : ChannelOwnerBase, IChannelOwner internal Task HarUnzipAsync(string zipFile, string harFile) => _channel.HarUnzipAsync(zipFile, harFile); - internal Task ConnectAsync(string wsEndpoint = default, IEnumerable> headers = default, float? slowMo = default, float? timeout = default) - => _channel.ConnectAsync(wsEndpoint, headers, slowMo, timeout); + internal Task ConnectAsync(string wsEndpoint = default, IEnumerable> headers = default, float? slowMo = default, float? timeout = default, string exposeNetwork = default) + => _channel.ConnectAsync(wsEndpoint, headers, slowMo, timeout, exposeNetwork); internal void AddStackToTracingNoReply(List stack, int id) => _channel.AddStackToTracingNoReply(stack, id); diff --git a/src/Playwright/Core/Locator.cs b/src/Playwright/Core/Locator.cs index 6861f568..46cf800c 100644 --- a/src/Playwright/Core/Locator.cs +++ b/src/Playwright/Core/Locator.cs @@ -177,7 +177,7 @@ internal class Locator : ILocator { throw new ArgumentException("Locators must belong to the same frame."); } - return new Locator(_frame, $"{_selector} >> {locatorImpl._selector}", options); + return new Locator(_frame, $"{_selector} >> internal:chain={JsonSerializer.Serialize(locatorImpl._selector, _locatorSerializerOptions)}", options); } IFrameLocator ILocator.FrameLocator(string selector) => diff --git a/src/Playwright/Helpers/StringExtensions.cs b/src/Playwright/Helpers/StringExtensions.cs index d5773b91..930fa3ae 100644 --- a/src/Playwright/Helpers/StringExtensions.cs +++ b/src/Playwright/Helpers/StringExtensions.cs @@ -687,6 +687,12 @@ internal static class StringExtensions case '?': tokens.Add("."); break; + case '[': + tokens.Add("["); + break; + case ']': + tokens.Add("]"); + break; case '{': inGroup = true; tokens.Add("("); diff --git a/src/Playwright/Transport/Channels/LocalUtilsChannel.cs b/src/Playwright/Transport/Channels/LocalUtilsChannel.cs index 22e36da5..024e7371 100644 --- a/src/Playwright/Transport/Channels/LocalUtilsChannel.cs +++ b/src/Playwright/Transport/Channels/LocalUtilsChannel.cs @@ -89,7 +89,7 @@ internal class LocalUtilsChannel : Channel { "harFile", harFile }, }); - internal async Task ConnectAsync(string wsEndpoint, IEnumerable> headers, float? slowMo, float? timeout) + internal async Task ConnectAsync(string wsEndpoint, IEnumerable> headers, float? slowMo, float? timeout, string exposeNetwork) { var args = new Dictionary { @@ -97,6 +97,7 @@ internal class LocalUtilsChannel : Channel { "headers", headers }, { "slowMo", slowMo }, { "timeout", timeout }, + { "exposeNetwork", exposeNetwork }, }; return (await Connection.SendMessageToServerAsync(Guid, "connect", args).ConfigureAwait(false)).Value.GetObject("pipe", Connection); }