diff --git a/src/Common/Version.props b/src/Common/Version.props index 6707fb3df..209b68764 100644 --- a/src/Common/Version.props +++ b/src/Common/Version.props @@ -2,7 +2,7 @@ 1.65.0 $(AssemblyVersion) - 5ccfbd85521679ebf5ab4d836852bf1c544aa748 + 8534271f0413bae6a455b7cf43b2637391bc614b diff --git a/src/Playwright.Tests/SelectorsGetByTests.cs b/src/Playwright.Tests/SelectorsGetByTests.cs index f5fd17de1..f194db201 100644 --- a/src/Playwright.Tests/SelectorsGetByTests.cs +++ b/src/Playwright.Tests/SelectorsGetByTests.cs @@ -98,6 +98,7 @@ public async Task GetByTextShouldWork() StringAssert.Contains(">Hello", await Page.GetByText("Hello", new() { Exact = true }).EvaluateAsync("e => e.outerHTML")); } + // See https://github.com/microsoft/playwright/issues/43898 [PlaywrightTest("selector-get-by.spec.ts", "getByLabel should work")] public async Task GetByLabelShouldWork() { @@ -106,6 +107,25 @@ public async Task GetByLabelShouldWork() Assert.AreEqual(await Page.GetByLabel("Name").EvaluateAsync("e => e.nodeName"), "INPUT"); Assert.AreEqual(await Page.MainFrame.GetByLabel("Name").EvaluateAsync("e => e.nodeName"), "INPUT"); Assert.AreEqual(await Page.Locator("div").GetByLabel("Name").EvaluateAsync("e => e.nodeName"), "INPUT"); + + await Page.SetContentAsync(@" + Pick one + + "); + await Page.EvaluateAsync(@"() => { + customElements.define('my-field', class extends HTMLElement { + static formAssociated = true; + constructor() { + super(); + this.attachInternals(); + } + }); + }"); + Assert.AreEqual(new[] { "city", "street" }, await Page.EvaluateAsync("() => [...document.querySelectorAll('label')].map(label => label.control?.id)")); + Assert.AreEqual(new[] { "city" }, await Page.GetByLabel("City").EvaluateAllAsync("elements => elements.map(e => e.id)")); + Assert.AreEqual(new[] { "street" }, await Page.GetByLabel("Street").EvaluateAllAsync("elements => elements.map(e => e.id)")); + Assert.AreEqual(new[] { "city" }, await Page.GetByRole(AriaRole.Combobox, new() { Name = "City", Exact = true }).EvaluateAllAsync("elements => elements.map(e => e.id)")); + Assert.AreEqual(new[] { "street" }, await Page.GetByRole(AriaRole.Combobox, new() { Name = "Street", Exact = true }).EvaluateAllAsync("elements => elements.map(e => e.id)")); } [PlaywrightTest("selector-get-by.spec.ts", "getByLabel should work with nested elements")] diff --git a/src/Playwright/API/Generated/IBrowserContext.cs b/src/Playwright/API/Generated/IBrowserContext.cs index 7276218c4..c1f940e56 100644 --- a/src/Playwright/API/Generated/IBrowserContext.cs +++ b/src/Playwright/API/Generated/IBrowserContext.cs @@ -1015,12 +1015,19 @@ public partial interface IBrowserContext /// /// Whether to emulate network being offline for the browser context. - /// Offline emulation only affects requests that go through the browser's regular network - /// stack, such as page navigations, fetch(), XMLHttpRequest and WebSockets. - /// It does not affect WebRTC traffic: established RTCPeerConnections keep sending - /// and receiving media over UDP. To test WebRTC connection loss, interrupt the connection - /// outside the browser, for example by stopping the TURN server or using an OS-level - /// firewall. + /// Offline emulation affects HTTP requests, such as page navigations, fetch() + /// and XMLHttpRequest. + /// WebSocket behavior depends on the browser and whether the connection is already + /// established. Chromium and Firefox fail new WebSocket connections while offline, + /// but do not close already-open connections. Chromium can defer incoming messages + /// until the context goes back online, rather than lose them; Firefox can continue + /// delivering messages while offline. WebKit can allow both new WebSocket connections + /// and traffic on established connections while offline. Do not rely on offline emulation + /// to test WebSocket message loss or connection closure. + /// Offline emulation does not affect WebRTC traffic: established RTCPeerConnections + /// keep sending and receiving media over UDP. To test WebRTC connection loss, interrupt + /// the connection outside the browser, for example by stopping the TURN server or using + /// an OS-level firewall. /// Task SetOfflineAsync(bool offline); diff --git a/src/Playwright/API/Generated/IFrame.cs b/src/Playwright/API/Generated/IFrame.cs index 4e14b30ab..5c57e74dc 100644 --- a/src/Playwright/API/Generated/IFrame.cs +++ b/src/Playwright/API/Generated/IFrame.cs @@ -576,6 +576,10 @@ public partial interface IFrame /// Allows locating input elements by the text of the associated <label> /// or aria-labelledby element, or by the aria-label attribute. /// + /// + /// Associated <label> elements are also supported for form-associated + /// custom elements. + /// /// **Usage** /// /// For example, this method will find inputs by label "Username" and "Password" in @@ -595,6 +599,10 @@ public partial interface IFrame /// Allows locating input elements by the text of the associated <label> /// or aria-labelledby element, or by the aria-label attribute. /// + /// + /// Associated <label> elements are also supported for form-associated + /// custom elements. + /// /// **Usage** /// /// For example, this method will find inputs by label "Username" and "Password" in diff --git a/src/Playwright/API/Generated/IFrameLocator.cs b/src/Playwright/API/Generated/IFrameLocator.cs index a5a7bc848..c4decc0e0 100644 --- a/src/Playwright/API/Generated/IFrameLocator.cs +++ b/src/Playwright/API/Generated/IFrameLocator.cs @@ -123,6 +123,10 @@ public partial interface IFrameLocator /// Allows locating input elements by the text of the associated <label> /// or aria-labelledby element, or by the aria-label attribute. /// + /// + /// Associated <label> elements are also supported for form-associated + /// custom elements. + /// /// **Usage** /// /// For example, this method will find inputs by label "Username" and "Password" in @@ -142,6 +146,10 @@ public partial interface IFrameLocator /// Allows locating input elements by the text of the associated <label> /// or aria-labelledby element, or by the aria-label attribute. /// + /// + /// Associated <label> elements are also supported for form-associated + /// custom elements. + /// /// **Usage** /// /// For example, this method will find inputs by label "Username" and "Password" in diff --git a/src/Playwright/API/Generated/IFunctionAssertions.cs b/src/Playwright/API/Generated/IFunctionAssertions.cs new file mode 100644 index 000000000..ac67fdd31 --- /dev/null +++ b/src/Playwright/API/Generated/IFunctionAssertions.cs @@ -0,0 +1,80 @@ +/* + * 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.Threading.Tasks; + +namespace Microsoft.Playwright; + +/// +/// +/// The class provides assertion methods that retry +/// a block of code. A new instance of is created +/// by calling : +/// +/// +/// using Microsoft.Playwright;
+/// using Microsoft.Playwright.MSTest;
+///
+/// namespace PlaywrightTests;
+///
+/// [TestClass]
+/// public class ExampleTests : PageTest
+/// {
+/// [TestMethod]
+/// public async Task ApiEventuallySucceeds()
+/// {
+/// await Expect(async () =>
+/// {
+/// var response = await Page.APIRequest.GetAsync("https://api.example.com");
+/// Assert.AreEqual(200, response.Status);
+/// }).ToPassAsync();
+/// }
+/// } +///
+///
+public partial interface IFunctionAssertions +{ + /// + /// + /// Retries the callback until it completes without throwing an exception, or until + /// the timeout is reached. When the timeout is reached, throws a + /// with the last exception that the callback has thrown. + /// + /// **Usage** + /// + /// await Expect(async () =>
+ /// {
+ /// var response = await Page.APIRequest.GetAsync("https://api.example.com");
+ /// Assert.AreEqual(200, response.Status);
+ /// }).ToPassAsync(new()
+ /// {
+ /// // Probe, wait 1s, probe, wait 2s, probe, wait 10s, probe, wait 10s, probe
+ /// Intervals = [1_000, 2_000, 10_000],
+ /// Timeout = 60_000,
+ /// }); + ///
+ ///
+ /// Call options + Task ToPassAsync(FunctionAssertionsToPassOptions? options = default); +} diff --git a/src/Playwright/API/Generated/ILocator.cs b/src/Playwright/API/Generated/ILocator.cs index c681882ad..97bac7f36 100644 --- a/src/Playwright/API/Generated/ILocator.cs +++ b/src/Playwright/API/Generated/ILocator.cs @@ -728,6 +728,10 @@ public partial interface ILocator /// Allows locating input elements by the text of the associated <label> /// or aria-labelledby element, or by the aria-label attribute. ///
+ /// + /// Associated <label> elements are also supported for form-associated + /// custom elements. + /// /// **Usage** /// /// For example, this method will find inputs by label "Username" and "Password" in @@ -747,6 +751,10 @@ public partial interface ILocator /// Allows locating input elements by the text of the associated <label> /// or aria-labelledby element, or by the aria-label attribute. /// + /// + /// Associated <label> elements are also supported for form-associated + /// custom elements. + /// /// **Usage** /// /// For example, this method will find inputs by label "Username" and "Password" in diff --git a/src/Playwright/API/Generated/IPage.cs b/src/Playwright/API/Generated/IPage.cs index 91ba3ed21..588bf6c21 100644 --- a/src/Playwright/API/Generated/IPage.cs +++ b/src/Playwright/API/Generated/IPage.cs @@ -1051,6 +1051,10 @@ public partial interface IPage /// Allows locating input elements by the text of the associated <label> /// or aria-labelledby element, or by the aria-label attribute. /// + /// + /// Associated <label> elements are also supported for form-associated + /// custom elements. + /// /// **Usage** /// /// For example, this method will find inputs by label "Username" and "Password" in @@ -1070,6 +1074,10 @@ public partial interface IPage /// Allows locating input elements by the text of the associated <label> /// or aria-labelledby element, or by the aria-label attribute. /// + /// + /// Associated <label> elements are also supported for form-associated + /// custom elements. + /// /// **Usage** /// /// For example, this method will find inputs by label "Username" and "Password" in diff --git a/src/Playwright/API/Generated/IPlaywrightAssertions.cs b/src/Playwright/API/Generated/IPlaywrightAssertions.cs index e6ef582fb..a25deb791 100644 --- a/src/Playwright/API/Generated/IPlaywrightAssertions.cs +++ b/src/Playwright/API/Generated/IPlaywrightAssertions.cs @@ -22,6 +22,9 @@ * SOFTWARE. */ +using System; +using System.Threading.Tasks; + namespace Microsoft.Playwright; /// @@ -64,6 +67,20 @@ public partial interface IPlaywrightAssertions /// object to use for assertions. IAPIResponseAssertions Expect(IAPIResponse response); + /// + /// Creates a object for the given callback. + /// **Usage** + /// + /// await Expect(async () =>
+ /// {
+ /// var response = await Page.APIRequest.GetAsync("https://api.example.com");
+ /// Assert.AreEqual(200, response.Status);
+ /// }).ToPassAsync(); + ///
+ ///
+ /// Callback to retry. A synchronous Action is accepted as well. + IFunctionAssertions Expect(Func callback); + /// /// Creates a object for the given . /// **Usage** diff --git a/src/Playwright/API/Generated/Options/FunctionAssertionsToPassOptions.cs b/src/Playwright/API/Generated/Options/FunctionAssertionsToPassOptions.cs new file mode 100644 index 000000000..6f38d237b --- /dev/null +++ b/src/Playwright/API/Generated/Options/FunctionAssertionsToPassOptions.cs @@ -0,0 +1,63 @@ +/* + * 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.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Microsoft.Playwright; + +public class FunctionAssertionsToPassOptions +{ + public FunctionAssertionsToPassOptions() { } + + public FunctionAssertionsToPassOptions(FunctionAssertionsToPassOptions clone) + { + if (clone == null) + { + return; + } + + Intervals = clone.Intervals; + Timeout = clone.Timeout; + } + + /// + /// + /// Time to wait between the retries in milliseconds. The last interval is used for + /// all the following retries. Defaults to [100, 250, 500, 1000]. + /// + /// + [JsonPropertyName("intervals")] + public IEnumerable? Intervals { get; set; } + + /// + /// + /// Time to retry the callback for in milliseconds. Defaults to 30000, change + /// the default with SetDefaultExpectToPassTimeout. Pass 0 to disable + /// the timeout. + /// + /// + [JsonPropertyName("timeout")] + public float? Timeout { get; set; } +} diff --git a/src/Playwright/Assertions.cs b/src/Playwright/Assertions.cs index 12c51c9ad..222952ea6 100644 --- a/src/Playwright/Assertions.cs +++ b/src/Playwright/Assertions.cs @@ -22,6 +22,8 @@ * SOFTWARE. */ +using System; +using System.Threading.Tasks; using Microsoft.Playwright.Core; namespace Microsoft.Playwright; @@ -45,6 +47,8 @@ public static class Assertions public static IAPIResponseAssertions Expect(IAPIResponse response) => new APIResponseAssertions(response, false); + public static IFunctionAssertions Expect(Func callback) => new FunctionAssertions(callback); + /// /// Creates assertions that prefix any failure message with , providing /// extra context in test reports. diff --git a/src/Playwright/Core/FunctionAssertions.cs b/src/Playwright/Core/FunctionAssertions.cs new file mode 100644 index 000000000..557fd4c78 --- /dev/null +++ b/src/Playwright/Core/FunctionAssertions.cs @@ -0,0 +1,41 @@ +/* + * 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; + +namespace Microsoft.Playwright.Core; + +internal class FunctionAssertions : IFunctionAssertions +{ + private readonly Func _callback; + + public FunctionAssertions(Func callback) + { + _callback = callback ?? throw new ArgumentNullException(nameof(callback)); + } + + public Task ToPassAsync(FunctionAssertionsToPassOptions? options = default) + => throw new NotImplementedException(); +}