Skip to content

Commit 589fc20

Browse files
authored
Introduce aria label selector (#2002)
1 parent 910677b commit 589fc20

21 files changed

+888
-87
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using System.Xml.Linq;
5+
using PuppeteerSharp.Tests.Attributes;
6+
using PuppeteerSharp.Xunit;
7+
using Xunit;
8+
using Xunit.Abstractions;
9+
using static System.Net.Mime.MediaTypeNames;
10+
11+
namespace PuppeteerSharp.Tests.AriaQueryHandlerTests
12+
{
13+
[Collection(TestConstants.TestFixtureCollectionName)]
14+
public class ParseAriaSelectorTests : PuppeteerPageBaseTest
15+
{
16+
public ParseAriaSelectorTests(ITestOutputHelper output) : base(output)
17+
{
18+
}
19+
20+
public override async Task InitializeAsync()
21+
{
22+
await base.InitializeAsync();
23+
24+
await Page.SetContentAsync(@"
25+
<button id=""btn"" role=""button""> Submit button and some spaces </button>
26+
");
27+
}
28+
public override Task DisposeAsync()
29+
{
30+
return base.DisposeAsync();
31+
}
32+
33+
[PuppeteerTest("ariaqueryhandler.spec.ts", "parseAriaSelector", "should find button")]
34+
[SkipBrowserFact(skipFirefox: true)]
35+
public async Task ShouldFindButton()
36+
{
37+
async Task ExpectFound(IElementHandle button)
38+
{
39+
Assert.NotNull(button);
40+
var id = await button.EvaluateFunctionAsync<string>(@"(button) => {
41+
return button.id;
42+
}");
43+
Assert.Equal("btn", id);
44+
}
45+
46+
var button= await Page.QuerySelectorAsync("aria/Submit button and some spaces[role=\"button\"]");
47+
await ExpectFound(button);
48+
button = await Page.QuerySelectorAsync("aria/Submit button and some spaces[role='button']");
49+
await ExpectFound(button);
50+
button = await Page.QuerySelectorAsync("aria/ Submit button and some spaces[role=\"button\"]");
51+
await ExpectFound(button);
52+
button = await Page.QuerySelectorAsync("aria/Submit button and some spaces [role=\"button\"]");
53+
await ExpectFound(button);
54+
button = await Page.QuerySelectorAsync("aria/Submit button and some spaces [ role = \"button\" ] ");
55+
await ExpectFound(button);
56+
button = await Page.QuerySelectorAsync("aria/[role=\"button\"]Submit button and some spaces");
57+
await ExpectFound(button);
58+
button = await Page.QuerySelectorAsync("aria/Submit button [role=\"button\"]and some spaces");
59+
await ExpectFound(button);
60+
button = await Page.QuerySelectorAsync("aria/[name=\" Submit button and some spaces\"][role=\"button\"]");
61+
await ExpectFound(button);
62+
button = await Page.QuerySelectorAsync("aria/[name=' Submit button and some spaces'][role='button']");
63+
await ExpectFound(button);
64+
button = await Page.QuerySelectorAsync("aria/ignored[name=\"Submit button and some spaces\"][role=\"button\"]");
65+
await ExpectFound(button);
66+
var ex = await Assert.ThrowsAnyAsync<PuppeteerException>(() => Page.QuerySelectorAsync("aria/smth[smth=\"true\"]"));
67+
Assert.Equal("Unknown aria attribute \"smth\" in selector", ex.Message);
68+
}
69+
}
70+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection.PortableExecutable;
4+
using System.Threading.Tasks;
5+
using System.Xml.Linq;
6+
using PuppeteerSharp.Tests.Attributes;
7+
using PuppeteerSharp.Xunit;
8+
using Xunit;
9+
using Xunit.Abstractions;
10+
using static System.Net.Mime.MediaTypeNames;
11+
12+
namespace PuppeteerSharp.Tests.AriaQueryHandlerTests
13+
{
14+
[Collection(TestConstants.TestFixtureCollectionName)]
15+
public class QueryAllArrayTests : PuppeteerPageBaseTest
16+
{
17+
public QueryAllArrayTests(ITestOutputHelper output) : base(output)
18+
{
19+
}
20+
21+
[PuppeteerTest("ariaqueryhandler.spec.ts", "queryAllArray", "$$eval should handle many elements")]
22+
[SkipBrowserFact(skipFirefox: true)]
23+
public async Task EvalShouldHandleManyElements()
24+
{
25+
await Page.SetContentAsync("");
26+
await Page.EvaluateExpressionAsync(@"
27+
for (var i = 0; i <= 10000; i++) {
28+
const button = document.createElement('button');
29+
button.textContent = i;
30+
document.body.appendChild(button);
31+
}
32+
");
33+
var sum = await Page
34+
.QuerySelectorAllHandleAsync("aria/[role=\"button\"]")
35+
.EvaluateFunctionAsync<int>(@"buttons => buttons.reduce((acc, button) => acc + Number(button.textContent), 0)");
36+
37+
Assert.Equal(50005000, sum);
38+
}
39+
}
40+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection.PortableExecutable;
4+
using System.Threading.Tasks;
5+
using System.Xml.Linq;
6+
using PuppeteerSharp.Tests.Attributes;
7+
using PuppeteerSharp.Xunit;
8+
using Xunit;
9+
using Xunit.Abstractions;
10+
using static System.Net.Mime.MediaTypeNames;
11+
12+
namespace PuppeteerSharp.Tests.AriaQueryHandlerTests
13+
{
14+
[Collection(TestConstants.TestFixtureCollectionName)]
15+
public class QueryAllTests : PuppeteerPageBaseTest
16+
{
17+
public QueryAllTests(ITestOutputHelper output) : base(output)
18+
{
19+
}
20+
21+
[PuppeteerTest("ariaqueryhandler.spec.ts", "queryAll", "should find menu by name")]
22+
[SkipBrowserFact(skipFirefox: true)]
23+
public async Task ShouldFindMenuByName()
24+
{
25+
await Page.SetContentAsync(@"
26+
<div role=""menu"" id=""mnu1"" aria-label=""menu div""></div>
27+
<div role=""menu"" id=""mnu2"" aria-label=""menu div""></div>
28+
");
29+
var divs = await Page.QuerySelectorAllAsync("aria/menu div");
30+
var ids = await Task.WhenAll(divs.Select(div => div.EvaluateFunctionAsync<string>("div => div.id")));
31+
32+
Assert.Equal("mnu1, mnu2", String.Join(", ", ids));
33+
}
34+
}
35+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using System;
2+
using System.Linq;
3+
using System.Reflection.PortableExecutable;
4+
using System.Threading.Tasks;
5+
using System.Xml.Linq;
6+
using PuppeteerSharp.Tests.Attributes;
7+
using PuppeteerSharp.Xunit;
8+
using Xunit;
9+
using Xunit.Abstractions;
10+
using static System.Net.Mime.MediaTypeNames;
11+
12+
namespace PuppeteerSharp.Tests.AriaQueryHandlerTests
13+
{
14+
[Collection(TestConstants.TestFixtureCollectionName)]
15+
public class QueryOneTests : PuppeteerPageBaseTest
16+
{
17+
public QueryOneTests(ITestOutputHelper output) : base(output)
18+
{
19+
}
20+
21+
[PuppeteerTest("ariaqueryhandler.spec.ts", "queryOne", "should find button by role")]
22+
[SkipBrowserFact(skipFirefox: true)]
23+
public async Task ShouldFindButtonByRole()
24+
{
25+
await Page.SetContentAsync("<div id='div'><button id='btn' role='button'>Submit</button></div>");
26+
var button = await Page.QuerySelectorAsync("aria/[role='button']");
27+
var id = await button.EvaluateFunctionAsync("(button) => button.id");
28+
Assert.Equal("btn", id);
29+
}
30+
31+
[PuppeteerTest("ariaqueryhandler.spec.ts", "queryOne", "should find button by name and role")]
32+
[SkipBrowserFact(skipFirefox: true)]
33+
public async Task ShouldFindButtonNameAndByRole()
34+
{
35+
await Page.SetContentAsync("<div id='div'><button id='btn' role='button'>Submit</button></div>");
36+
var button = await Page.QuerySelectorAsync("aria/Submit[role='button']");
37+
var id = await button.EvaluateFunctionAsync("(button) => button.id");
38+
Assert.Equal("btn", id);
39+
}
40+
41+
[PuppeteerTest("ariaqueryhandler.spec.ts", "queryOne", "should find first matching element")]
42+
[SkipBrowserFact(skipFirefox: true)]
43+
public async Task ShouldFindFirstMatchingElement()
44+
{
45+
await Page.SetContentAsync(@"
46+
<div role=""menu"" id=""mnu1"" aria-label=""menu div""></div>
47+
<div role=""menu"" id=""mnu2"" aria-label=""menu div""></div>
48+
");
49+
var button = await Page.QuerySelectorAsync("aria/menu div");
50+
var id = await button.EvaluateFunctionAsync("(button) => button.id");
51+
Assert.Equal("mnu1", id);
52+
}
53+
54+
[PuppeteerTest("ariaqueryhandler.spec.ts", "queryOne", "should find by name")]
55+
[SkipBrowserFact(skipFirefox: true)]
56+
public async Task ShouldFindByName()
57+
{
58+
await Page.SetContentAsync(@"
59+
<div role=""menu"" id=""mnu1"" aria-label=""menu-label1"">menu div</div>
60+
<div role=""menu"" id=""mnu2"" aria-label=""menu-label2"">menu div</div>
61+
");
62+
var button = await Page.QuerySelectorAsync("aria/menu-label1");
63+
var id = await button.EvaluateFunctionAsync("(button) => button.id");
64+
Assert.Equal("mnu1", id);
65+
}
66+
67+
[PuppeteerTest("ariaqueryhandler.spec.ts", "queryOne", "should find by name")]
68+
[SkipBrowserFact(skipFirefox: true)]
69+
public async Task ShouldFindByName2()
70+
{
71+
await Page.SetContentAsync(@"
72+
<div role=""menu"" id=""mnu1"" aria-label=""menu-label1"">menu div</div>
73+
<div role=""menu"" id=""mnu2"" aria-label=""menu-label2"">menu div</div>
74+
");
75+
var button = await Page.QuerySelectorAsync("aria/menu-label2");
76+
var id = await button.EvaluateFunctionAsync("(button) => button.id");
77+
Assert.Equal("mnu2", id);
78+
}
79+
}
80+
}
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
using System;
2+
using System.Configuration;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
using System.Reflection.Metadata;
6+
using System.Reflection.PortableExecutable;
7+
using System.Threading.Tasks;
8+
using System.Xml.Linq;
9+
using Microsoft.AspNetCore.Hosting.Server;
10+
using PuppeteerSharp.Tests.Attributes;
11+
using PuppeteerSharp.Xunit;
12+
using SixLabors.ImageSharp;
13+
using Xunit;
14+
using Xunit.Abstractions;
15+
using static System.Net.Mime.MediaTypeNames;
16+
17+
namespace PuppeteerSharp.Tests.AriaQueryHandlerTests
18+
{
19+
[Collection(TestConstants.TestFixtureCollectionName)]
20+
public class WaitForSelectorAriaTests : PuppeteerPageBaseTest
21+
{
22+
public WaitForSelectorAriaTests(ITestOutputHelper output) : base(output)
23+
{
24+
}
25+
26+
const string addElement = @"(tag) => document.body.appendChild(document.createElement(tag))";
27+
28+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "should immediately resolve promise if node exists")]
29+
[SkipBrowserFact(skipFirefox: true)]
30+
public async Task ShouldImmediatelyResolvePromiseIfNodeExists()
31+
{
32+
await Page.GoToAsync(TestConstants.EmptyPage);
33+
await Page.EvaluateFunctionAsync(addElement, "button");
34+
await Page.WaitForSelectorAsync("aria/[role=\"button\"]");
35+
}
36+
37+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "should work for ElementHandle.waitForSelector")]
38+
[SkipBrowserFact(skipFirefox: true)]
39+
public async Task ShouldWorkForElementHandleWaitForSelector()
40+
{
41+
await Page.GoToAsync(TestConstants.EmptyPage);
42+
await Page.EvaluateFunctionAsync(
43+
@"() => {
44+
return (document.body.innerHTML = `<div><button>test</button></div>`);
45+
}");
46+
var element = Page.QuerySelectorAsync("div");
47+
await Page.WaitForSelectorAsync("aria/test");
48+
}
49+
50+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "should persist query handler bindings across reloads")]
51+
[SkipBrowserFact(skipFirefox: true)]
52+
public async Task ShouldPersistQueryHandlerBindingsAcrossReloads()
53+
{
54+
await Page.GoToAsync(TestConstants.EmptyPage);
55+
await Page.EvaluateFunctionAsync(addElement, "button");
56+
await Page.WaitForSelectorAsync("aria/[role=\"button\"]");
57+
await Page.ReloadAsync();
58+
await Page.EvaluateFunctionAsync(addElement, "button");
59+
await Page.WaitForSelectorAsync("aria/[role=\"button\"]");
60+
}
61+
62+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "should persist query handler bindings across navigations")]
63+
[SkipBrowserFact(skipFirefox: true)]
64+
public async Task ShouldPersistQueryHandlerBindingsAcrossNavigations()
65+
{
66+
await Page.GoToAsync("data:text/html,");
67+
await Page.EvaluateFunctionAsync(addElement, "button");
68+
await Page.WaitForSelectorAsync("aria/[role=\"button\"]");
69+
await Page.GoToAsync("data:text/html,");
70+
await Page.EvaluateFunctionAsync(addElement, "button");
71+
await Page.WaitForSelectorAsync("aria/[role=\"button\"]");
72+
}
73+
74+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "should work independently of `exposeFunction")]
75+
[SkipBrowserFact(skipFirefox: true)]
76+
public async Task ShouldWorkIndependentlyOfExposeFunction()
77+
{
78+
await Page.GoToAsync(TestConstants.EmptyPage);
79+
await Page.ExposeFunctionAsync("ariaQuerySelector", new Func<int, int, int>((a, b) => a + b));
80+
await Page.EvaluateFunctionAsync(addElement, "button");
81+
await Page.WaitForSelectorAsync("aria/[role=\"button\"]");
82+
var result = await Page.EvaluateExpressionAsync<int>("globalThis.ariaQuerySelector(2,8)");
83+
Assert.Equal(10, result);
84+
}
85+
86+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "should work with removed MutationObserver")]
87+
[SkipBrowserFact(skipFirefox: true)]
88+
public async Task ShouldWorkWithRemovedMutationObserver()
89+
{
90+
await Page.EvaluateFunctionAsync(@"() => delete window.MutationObserver");
91+
var handleTask = Page.WaitForSelectorAsync("aria/anything");
92+
await Task.WhenAll(
93+
handleTask,
94+
Page.SetContentAsync("<h1>anything</h1>"));
95+
Assert.NotNull(handleTask.Result);
96+
Assert.Equal("anything", await Page.EvaluateFunctionAsync("x => x.textContent", handleTask.Result));
97+
}
98+
99+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "should resolve promise when node is added")]
100+
[SkipBrowserFact(skipFirefox: true)]
101+
public async Task ShouldResolvePromiseWhenNodeIsAdded()
102+
{
103+
await Page.GoToAsync(TestConstants.EmptyPage);
104+
var frame = Page.MainFrame;
105+
var watchdog = frame.WaitForSelectorAsync("aria/[role=\"heading\"]");
106+
await frame.EvaluateFunctionAsync(addElement, "br");
107+
await frame.EvaluateFunctionAsync(addElement, "h1");
108+
var elementHandle = await watchdog;
109+
var tagName = await (
110+
await elementHandle.GetPropertyAsync("tagName")
111+
).JsonValueAsync();
112+
Assert.Equal("H1", tagName);
113+
}
114+
115+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "should work when node is added through innerHTML")]
116+
[SkipBrowserFact(skipFirefox: true)]
117+
public async Task ShouldWorkWhenNodeIsAddedThroughInnerHTML()
118+
{
119+
await Page.GoToAsync(TestConstants.EmptyPage);
120+
var watchdog = Page.WaitForSelectorAsync("aria/name");
121+
await Page.EvaluateFunctionAsync(addElement, "span");
122+
await Page.EvaluateFunctionAsync(@"() => {
123+
return (document.querySelector('span').innerHTML =
124+
'<h3><div aria-label=""name""></div></h3>');
125+
}");
126+
await watchdog;
127+
}
128+
129+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "Page.waitForSelector is shortcut for main frame")]
130+
[SkipBrowserFact(skipFirefox: true)]
131+
public async Task PageWaitForSelectorIsShortcutForMainFrame()
132+
{
133+
await Page.GoToAsync(TestConstants.EmptyPage);
134+
await FrameUtils.AttachFrameAsync(Page, "frame1", TestConstants.EmptyPage);
135+
var otherFrame = Page.FirstChildFrame();
136+
var watchdog = Page.WaitForSelectorAsync("aria/[role=\"button\"]");
137+
await otherFrame.EvaluateFunctionAsync(addElement, "button");
138+
await Page.EvaluateFunctionAsync(addElement, "button");
139+
var elementHandle = await watchdog;
140+
Assert.Same(elementHandle.ExecutionContext.Frame, Page.MainFrame);
141+
}
142+
143+
[PuppeteerTest("ariaqueryhandler.spec.ts", "waitForSelector (aria)", "should run in specified frame")]
144+
[SkipBrowserFact(skipFirefox: true)]
145+
public async Task ShouldRunInSpecifiedFrame()
146+
{
147+
await FrameUtils.AttachFrameAsync(Page, "frame1", TestConstants.EmptyPage);
148+
await FrameUtils.AttachFrameAsync(Page, "frame2", TestConstants.EmptyPage);
149+
var frame1 = Page.Frames.First(frame => frame.Name == "frame1");
150+
var frame2 = Page.Frames.First(frame => frame.Name == "frame2");
151+
var waitForSelectorTask = frame2.WaitForSelectorAsync("aria/[role=\"button\"]");
152+
await frame1.EvaluateFunctionAsync(addElement, "button");
153+
await frame2.EvaluateFunctionAsync(addElement, "button");
154+
var elementHandle = await waitForSelectorTask;
155+
Assert.Same(elementHandle.ExecutionContext.Frame, frame2);
156+
}
157+
}
158+
}

0 commit comments

Comments
 (0)