diff --git a/src/Playwright.Tests/Locator/LocatorQueryTests.cs b/src/Playwright.Tests/Locator/LocatorQueryTests.cs new file mode 100644 index 0000000000..af0996f77c --- /dev/null +++ b/src/Playwright.Tests/Locator/LocatorQueryTests.cs @@ -0,0 +1,85 @@ +/* + * 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; +using NUnit.Framework; + +namespace Microsoft.Playwright.Tests.Locator +{ + public class LocatorQueryTests : PageTestEx + { + [PlaywrightTest("locator-query.spec.ts", "should respect first() and last()")] + public async Task ShouldRespectFirstAndLast() + { + await Page.SetContentAsync(@" +
+

A

+

A

A

+

A

A

A

+
"); + Assert.AreEqual(6, await Page.Locator("div >> p").CountAsync()); + Assert.AreEqual(6, await Page.Locator("div").Locator("p").CountAsync()); + Assert.AreEqual(1, await Page.Locator("div").First.Locator("p").CountAsync()); + Assert.AreEqual(3, await Page.Locator("div").Last.Locator("p").CountAsync()); + } + + [PlaywrightTest("locator-query.spec.ts", "should respect nth()")] + public async Task ShouldRespectNth() + { + await Page.SetContentAsync(@" +
+

A

+

A

A

+

A

A

A

+
"); + Assert.AreEqual(1, await Page.Locator("div >> p").Nth(0).CountAsync()); + Assert.AreEqual(2, await Page.Locator("div").Nth(1).Locator("p").CountAsync()); + Assert.AreEqual(3, await Page.Locator("div").Nth(2).Locator("p").CountAsync()); + } + + [PlaywrightTest("locator-query.spec.ts", "should throw on capture w/ nth()")] + public async Task ShouldThrowOnCaptureWithNth() + { + await Page.SetContentAsync("

A

"); + var exception = await PlaywrightAssert.ThrowsAsync(() => Page.Locator("*css=div >> p").Nth(1).ClickAsync()); + StringAssert.Contains("Can't query n-th element", exception.Message); + } + + [PlaywrightTest("locator-query.spec.ts", "should throw on due to strictness")] + public async Task ShouldThrowDueToStrictness() + { + await Page.SetContentAsync("
A
B
"); + var exception = await PlaywrightAssert.ThrowsAsync(() => Page.Locator("div").IsVisibleAsync()); + StringAssert.Contains("strict mode violation", exception.Message); + } + + [PlaywrightTest("locator-query.spec.ts", "should throw on due to strictness 2")] + public async Task ShouldThrowDueToStrictness2() + { + await Page.SetContentAsync(""); + var exception = await PlaywrightAssert.ThrowsAsync(() => Page.Locator("option").EvaluateAsync("() => {}")); + StringAssert.Contains("strict mode violation", exception.Message); + } + } +} diff --git a/src/Playwright/API/Supplements/ILocator.cs b/src/Playwright/API/Supplements/ILocator.cs new file mode 100644 index 0000000000..0d57c2103e --- /dev/null +++ b/src/Playwright/API/Supplements/ILocator.cs @@ -0,0 +1,34 @@ +/* + * 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; +using System.Threading.Tasks; + +namespace Microsoft.Playwright +{ + public partial interface ILocator + { + Task EvaluateAsync(string expression, object arg = null, LocatorEvaluateOptions options = null); + } +} diff --git a/src/Playwright/Core/Locator.cs b/src/Playwright/Core/Locator.cs index a7a4ed7237..9a8fa794a0 100644 --- a/src/Playwright/Core/Locator.cs +++ b/src/Playwright/Core/Locator.cs @@ -43,9 +43,9 @@ public Locator(Frame parent, string selector) _selector = selector; } - public ILocator First => new Locator(_frame, $"{_selector} >> _nth=first"); + public ILocator First => new Locator(_frame, $"{_selector} >> nth=0"); - public ILocator Last => new Locator(_frame, $"{_selector} >> _nth=last"); + public ILocator Last => new Locator(_frame, $"{_selector} >> nth=-1"); public async Task> AllInnerTextsAsync() { @@ -121,6 +121,9 @@ public Task> ElementHandlesAsync() public Task EvaluateAllAsync(string expression, object arg = null) => _frame.EvalOnSelectorAllAsync(_selector, expression, arg); + public Task EvaluateAsync(string expression, object arg = null, LocatorEvaluateOptions options = null) + => EvaluateAsync(expression, arg, options); + public Task EvaluateAsync(string expression, object arg = null, LocatorEvaluateOptions options = null) => _frame.EvalOnSelectorAsync(_selector, expression, arg, ConvertOptions(options)); @@ -167,7 +170,7 @@ public Task IsVisibleAsync(LocatorIsVisibleOptions options = null) => _frame.IsVisibleAsync(_selector, ConvertOptions(options)); public ILocator Nth(int index) - => new Locator(_frame, $"{_selector} >> _nth=${index}"); + => new Locator(_frame, $"{_selector} >> nth={index}"); public Task PressAsync(string key, LocatorPressOptions options = null) => _frame.PressAsync(_selector, key, ConvertOptions(options));