Skip to content

Commit f15b4dc

Browse files
Meir017kblok
authored andcommitted
Implement Page.GetElementAsync (a.k.a Page.$eval) (#189)
1 parent aaa2710 commit f15b4dc

File tree

6 files changed

+86
-24
lines changed

6 files changed

+86
-24
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.Threading.Tasks;
2+
using Xunit;
3+
4+
namespace PuppeteerSharp.Tests.Page
5+
{
6+
[Collection("PuppeteerLoaderFixture collection")]
7+
public class EvalTests : PuppeteerPageBaseTest
8+
{
9+
[Fact]
10+
public async Task ShouldWork()
11+
{
12+
await Page.SetContentAsync("<section id='testAttribute'>43543</section>");
13+
var idAttribute = await Page.GetElementAsync("section").EvaluateFunctionAsync<string>("e => e.id");
14+
Assert.Equal("testAttribute", idAttribute);
15+
}
16+
17+
[Fact]
18+
public async Task ShouldAcceptArguments()
19+
{
20+
await Page.SetContentAsync("<section>hello</section>");
21+
var text = await Page.GetElementAsync("section").EvaluateFunctionAsync<string>("(e, suffix) => e.textContent + suffix", " world!");
22+
Assert.Equal("hello world!", text);
23+
}
24+
25+
[Fact]
26+
public async Task ShouldAcceptElementHandlesAsArguments()
27+
{
28+
await Page.SetContentAsync("<section>hello</section><div> world</div>");
29+
var divHandle = await Page.GetElementAsync("div");
30+
var text = await Page.GetElementAsync("section").EvaluateFunctionAsync<string>("(e, div) => e.textContent + div.textContent", divHandle);
31+
Assert.Equal("hello world", text);
32+
}
33+
34+
[Fact]
35+
public async Task ShouldThrowErrorIfNoElementIsFound()
36+
{
37+
var exception = await Assert.ThrowsAsync<SelectorException>(()
38+
=> Page.GetElementAsync("section").EvaluateFunctionAsync<string>("e => e.id"));
39+
Assert.Contains("failed to find element matching selector", exception.Message);
40+
}
41+
}
42+
}

lib/PuppeteerSharp/ElementHandle.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ namespace PuppeteerSharp
77
{
88
public class ElementHandle : JSHandle
99
{
10-
private readonly Page _page;
10+
internal Page Page { get; }
1111

1212
public ElementHandle(ExecutionContext context, Session client, object remoteObject, Page page) :
1313
base(context, client, remoteObject)
1414
{
15-
_page = page;
15+
Page = page;
1616
}
1717

1818
public override ElementHandle AsElement() => this;
@@ -24,7 +24,7 @@ public ElementHandle(ExecutionContext context, Session client, object remoteObje
2424
public async Task HoverAsync()
2525
{
2626
var (x, y) = await VisibleCenterAsync();
27-
await _page.Mouse.MoveAsync(x, y);
27+
await Page.Mouse.MoveAsync(x, y);
2828
}
2929

3030
/// <summary>
@@ -36,7 +36,7 @@ public async Task HoverAsync()
3636
public async Task ClickAsync(ClickOptions options = null)
3737
{
3838
var (x, y) = await VisibleCenterAsync();
39-
await _page.Mouse.ClickAsync(x, y, options);
39+
await Page.Mouse.ClickAsync(x, y, options);
4040
}
4141

4242
/// <summary>
@@ -60,7 +60,7 @@ public async Task UploadFileAsync(params string[] filePaths)
6060
public async Task TapAsync()
6161
{
6262
var (x, y) = await VisibleCenterAsync();
63-
await _page.Touchscreen.TapAsync(x, y);
63+
await Page.Touchscreen.TapAsync(x, y);
6464
}
6565

6666
/// <summary>
@@ -93,7 +93,7 @@ public async Task TapAsync()
9393
public async Task TypeAsync(string text, TypeOptions options = null)
9494
{
9595
await FocusAsync();
96-
await _page.Keyboard.TypeAsync(text, options);
96+
await Page.Keyboard.TypeAsync(text, options);
9797
}
9898

9999
/// <summary>
@@ -108,7 +108,7 @@ public async Task TypeAsync(string text, TypeOptions options = null)
108108
public async Task PressAsync(string key, PressOptions options = null)
109109
{
110110
await FocusAsync();
111-
await _page.Keyboard.PressAsync(key, options);
111+
await Page.Keyboard.PressAsync(key, options);
112112
}
113113

114114
internal async Task<ElementHandle> GetElementAsync(string selector)

lib/PuppeteerSharp/Extensions.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System.Threading.Tasks;
2+
3+
namespace PuppeteerSharp
4+
{
5+
public static class Extensions
6+
{
7+
/// <summary>
8+
/// Runs <paramref name="pageFunction"/> within the frame and passes it the outcome of <paramref name="elementHandleTask"/> as the first argument
9+
/// </summary>
10+
/// <typeparam name="T">The type of the response</typeparam>
11+
/// <param name="elementHandleTask">A task that returns an <see cref="ElementHandle"/> that will be used as the first argument in <paramref name="pageFunction"/></param>
12+
/// <param name="pageFunction">Function to be evaluated in browser context</param>
13+
/// <param name="args">Arguments to pass to <c>pageFunction</c></param>
14+
/// <returns>Task which resolves to the return value of <c>pageFunction</c></returns>
15+
/// <exception cref="SelectorException">If <paramref name="elementHandleTask"/> resolves to <c>null</c></exception>
16+
public static async Task<T> EvaluateFunctionAsync<T>(this Task<ElementHandle> elementHandleTask, string pageFunction, params object[] args)
17+
{
18+
var elementHandle = await elementHandleTask;
19+
if (elementHandle == null)
20+
{
21+
throw new SelectorException($"Error: failed to find element matching selector");
22+
}
23+
24+
var newArgs = new object[args.Length + 1];
25+
newArgs[0] = elementHandle;
26+
args.CopyTo(newArgs, 1);
27+
var result = await elementHandle.Page.EvaluateFunctionAsync<T>(pageFunction, newArgs);
28+
await elementHandle.DisposeAsync();
29+
return result;
30+
}
31+
}
32+
}

lib/PuppeteerSharp/Frame.cs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,7 @@ internal async Task<ElementHandle> GetElementAsync(string selector)
128128
var value = await document.GetElementAsync(selector);
129129
return value;
130130
}
131-
132-
internal Task<object> Eval(string selector, Func<object> pageFunction, object[] args)
133-
{
134-
throw new NotImplementedException();
135-
}
136-
137-
internal Task<object> Eval(string selector, string pageFunction, object[] args)
138-
{
139-
throw new NotImplementedException();
140-
}
141-
131+
142132
internal Task<object> EvalMany(string selector, Func<object> pageFunction, object[] args)
143133
{
144134
throw new NotImplementedException();

lib/PuppeteerSharp/Page.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,12 +230,6 @@ public async Task<JSHandle> QueryObjects(JSHandle prototypeHandle)
230230
return await context.QueryObjects(prototypeHandle);
231231
}
232232

233-
public async Task<object> EvalAsync(string selector, Func<object> pageFunction, params object[] args)
234-
=> await MainFrame.Eval(selector, pageFunction, args);
235-
236-
public async Task<object> EvalAsync(string selector, string pageFunction, params object[] args)
237-
=> await MainFrame.Eval(selector, pageFunction, args);
238-
239233
public async Task SetRequestInterceptionAsync(bool value)
240234
=> await _networkManager.SetRequestInterceptionAsync(value);
241235

lib/PuppeteerSharp/SelectorException.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ public class SelectorException : PuppeteerException
77
{
88
public string Selector { get; }
99

10+
public SelectorException(string message) : base(message)
11+
{
12+
}
13+
1014
public SelectorException(string message, string selector) : base(message)
1115
{
1216
Selector = selector;

0 commit comments

Comments
 (0)