Skip to content

Commit 7a00360

Browse files
Meir017kblok
authored andcommitted
Implement Page.XPathAsync (a.k.a. Page.$x) (#200)
1 parent 7a6a42c commit 7a00360

File tree

4 files changed

+72
-10
lines changed

4 files changed

+72
-10
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System.Threading.Tasks;
2+
using Xunit;
3+
4+
namespace PuppeteerSharp.Tests.Page
5+
{
6+
[Collection("PuppeteerLoaderFixture collection")]
7+
public class XPathTests : PuppeteerPageBaseTest
8+
{
9+
[Fact]
10+
public async Task ShouldQueryExistingElement()
11+
{
12+
await Page.SetContentAsync("<section>test</section>");
13+
var elements = await Page.XPathAsync("/html/body/section");
14+
Assert.NotNull(elements[0]);
15+
Assert.Single(elements);
16+
}
17+
18+
[Fact]
19+
public async Task ShouldReturnEmptyArrayForNonExistingElement()
20+
{
21+
var elements = await Page.XPathAsync("/html/body/non-existing-element");
22+
Assert.Empty(elements);
23+
}
24+
25+
[Fact]
26+
public async Task ShouldReturnMultipleElements()
27+
{
28+
await Page.SetContentAsync("<div></div><div></div>");
29+
var elements = await Page.XPathAsync("/html/body/div");
30+
Assert.Equal(2, elements.Length);
31+
}
32+
}
33+
}

lib/PuppeteerSharp/ElementHandle.cs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,28 @@ internal async Task<ElementHandle[]> QuerySelectorAllAsync(string selector)
136136

137137
var properties = await arrayHandle.GetPropertiesAsync();
138138
await arrayHandle.DisposeAsync();
139-
var result = new List<ElementHandle>();
140-
foreach(var property in properties.Values)
141-
{
142-
var elementHandle = property.AsElement();
143-
if(elementHandle != null)
144-
{
145-
result.Add(elementHandle);
146-
}
147-
}
148-
return result.ToArray();
139+
140+
return properties.Values.OfType<ElementHandle>().ToArray();
141+
}
142+
143+
internal async Task<ElementHandle[]> XPathAsync(string expression)
144+
{
145+
var arrayHandle = await ExecutionContext.EvaluateFunctionHandleAsync(
146+
@"(element, expression) => {
147+
const document = element.ownerDocument || element;
148+
const iterator = document.evaluate(expression, element, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
149+
const array = [];
150+
let item;
151+
while ((item = iterator.iterateNext()))
152+
array.push(item);
153+
return array;
154+
}",
155+
this, expression
156+
);
157+
var properties = await arrayHandle.GetPropertiesAsync();
158+
await arrayHandle.DisposeAsync();
159+
160+
return properties.Values.OfType<ElementHandle>().ToArray();
149161
}
150162

151163
private async Task<(decimal x, decimal y)> VisibleCenterAsync()

lib/PuppeteerSharp/Frame.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ internal async Task<ElementHandle[]> QuerySelectorAllAsync(string selector)
152152
return value;
153153
}
154154

155+
internal async Task<ElementHandle[]> XPathAsync(string expression)
156+
{
157+
var document = await GetDocument();
158+
var value = await document.XPathAsync(expression);
159+
return value;
160+
}
161+
155162
internal Task<ElementHandle> AddStyleTag(dynamic options)
156163
{
157164
throw new NotImplementedException();

lib/PuppeteerSharp/Page.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,16 @@ public Task<ElementHandle[]> QuerySelectorAllAsync(string selector)
188188
public Task<JSHandle> QuerySelectorAllHandleAsync(string selector)
189189
=> EvaluateFunctionHandleAsync("selector => Array.from(document.querySelectorAll(selector))", selector);
190190

191+
/// <summary>
192+
/// Evaluates the XPath expression
193+
/// </summary>
194+
/// <param name="expression">Expression to evaluate <see cref="https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate"/></param>
195+
/// <returns>Task which resolves to an array of <see cref="ElementHandle"/></returns>
196+
/// <remarks>
197+
/// Shortcut for <c>page.MainFrame.XPathAsync(expression)</c>
198+
/// </remarks>
199+
public Task<ElementHandle[]> XPathAsync(string expression) => MainFrame.XPathAsync(expression);
200+
191201
/// <summary>
192202
/// Executes a script in browser context
193203
/// </summary>

0 commit comments

Comments
 (0)