Skip to content

Commit b37ef47

Browse files
Meir017kblok
authored andcommitted
Implemented all JSHandle tests (#204)
1 parent 7022ca1 commit b37ef47

File tree

10 files changed

+216
-13
lines changed

10 files changed

+216
-13
lines changed

lib/PuppeteerSharp.Tests/Frame/WaitForFunctionTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public async Task ShouldThrowNegativePollingInterval()
5656
[Fact]
5757
public async Task ShouldReturnTheSuccessValueAsAJSHandle()
5858
{
59-
Assert.Equal(5, await (await Page.WaitForFunctionAsync("() => 5")).JsonValue<int>());
59+
Assert.Equal(5, await (await Page.WaitForFunctionAsync("() => 5")).JsonValueAsync<int>());
6060
}
6161

6262
[Fact]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System.Threading.Tasks;
2+
using Xunit;
3+
4+
namespace PuppeteerSharp.Tests.JSHandleTests
5+
{
6+
[Collection("PuppeteerLoaderFixture collection")]
7+
public class AsElementTests : PuppeteerPageBaseTest
8+
{
9+
[Fact]
10+
public async Task ShouldWork()
11+
{
12+
var aHandle = await Page.EvaluateExpressionHandleAsync("document.body");
13+
var element = aHandle.AsElement();
14+
Assert.NotNull(element);
15+
}
16+
17+
[Fact]
18+
public async Task ShouldReturnNullForNonElements()
19+
{
20+
var aHandle = await Page.EvaluateExpressionHandleAsync("2");
21+
var element = aHandle.AsElement();
22+
Assert.Null(element);
23+
}
24+
25+
[Fact]
26+
public async Task ShouldReturnElementHandleForTextNodes()
27+
{
28+
await Page.SetContentAsync("<div>ee!</div>");
29+
var aHandle = await Page.EvaluateExpressionHandleAsync("document.querySelector('div').firstChild");
30+
var element = aHandle.AsElement();
31+
Assert.NotNull(element);
32+
Assert.NotNull(await Page.EvaluateFunctionAsync("e => e.nodeType === HTMLElement.TEXT_NODE", element));
33+
}
34+
}
35+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using System.Threading.Tasks;
2+
using Xunit;
3+
4+
namespace PuppeteerSharp.Tests.JSHandleTests
5+
{
6+
[Collection("PuppeteerLoaderFixture collection")]
7+
public class GetPropertiesTests : PuppeteerPageBaseTest
8+
{
9+
[Fact]
10+
public async Task ShouldWork()
11+
{
12+
var aHandle = await Page.EvaluateExpressionHandleAsync(@"({
13+
foo: 'bar'
14+
})");
15+
var properties = await aHandle.GetPropertiesAsync();
16+
properties.TryGetValue("foo", out var foo);
17+
Assert.NotNull(foo);
18+
Assert.Equal("bar", await foo.JsonValueAsync<string>());
19+
}
20+
21+
[Fact]
22+
public async Task ShouldReturnEvenNonOwnProperties()
23+
{
24+
var aHandle = await Page.EvaluateFunctionHandleAsync(@"() => {
25+
class A {
26+
constructor() {
27+
this.a = '1';
28+
}
29+
}
30+
class B extends A {
31+
constructor() {
32+
super();
33+
this.b = '2';
34+
}
35+
}
36+
return new B();
37+
}");
38+
var properties = await aHandle.GetPropertiesAsync();
39+
Assert.Equal("1", await properties["a"].JsonValueAsync<string>());
40+
Assert.Equal("2", await properties["b"].JsonValueAsync<string>());
41+
}
42+
}
43+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Threading.Tasks;
2+
using Xunit;
3+
4+
namespace PuppeteerSharp.Tests.JSHandleTests
5+
{
6+
[Collection("PuppeteerLoaderFixture collection")]
7+
public class GetPropertyTests : PuppeteerPageBaseTest
8+
{
9+
[Fact]
10+
public async Task ShouldWork()
11+
{
12+
var aHandle = await Page.EvaluateExpressionHandleAsync(@"({
13+
one: 1,
14+
two: 2,
15+
three: 3
16+
})");
17+
var twoHandle = await aHandle.GetPropertyAsync("two");
18+
Assert.Equal(2, await twoHandle.JsonValueAsync<int>());
19+
}
20+
}
21+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Newtonsoft.Json.Linq;
2+
using System.Threading.Tasks;
3+
using Xunit;
4+
5+
namespace PuppeteerSharp.Tests.JSHandleTests
6+
{
7+
[Collection("PuppeteerLoaderFixture collection")]
8+
public class JsonValueTests : PuppeteerPageBaseTest
9+
{
10+
[Fact]
11+
public async Task ShouldWork()
12+
{
13+
var aHandle = await Page.EvaluateExpressionHandleAsync("({ foo: 'bar'})");
14+
var json = await aHandle.JsonValueAsync();
15+
Assert.Equal(JObject.Parse("{ foo: 'bar' }"), json);
16+
}
17+
18+
[Fact]
19+
public async Task ShouldNotWorkWithDates()
20+
{
21+
var dateHandle = await Page.EvaluateExpressionHandleAsync("new Date('2017-09-26T00:00:00.000Z')");
22+
var json = await dateHandle.JsonValueAsync();
23+
Assert.Equal(JObject.Parse("{}"), json);
24+
}
25+
26+
[Fact]
27+
public async Task ShouldThrowForCircularObjects()
28+
{
29+
var windowHandle = await Page.EvaluateExpressionHandleAsync("window");
30+
var exception = await Assert.ThrowsAsync<MessageException>(()
31+
=> windowHandle.JsonValueAsync());
32+
Assert.Contains("Object reference chain is too long", exception.Message);
33+
}
34+
}
35+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Threading.Tasks;
2+
using Xunit;
3+
4+
namespace PuppeteerSharp.Tests.JSHandleTests
5+
{
6+
[Collection("PuppeteerLoaderFixture collection")]
7+
public class ToStringTests : PuppeteerPageBaseTest
8+
{
9+
[Fact]
10+
public async Task ShouldWorkForPrimitives()
11+
{
12+
var numberHandle = await Page.EvaluateExpressionHandleAsync("2");
13+
Assert.Equal("JSHandle:2", numberHandle.ToString());
14+
var stringHandle = await Page.EvaluateExpressionHandleAsync("'a'");
15+
Assert.Equal("JSHandle:a", stringHandle.ToString());
16+
}
17+
18+
[Fact]
19+
public async Task ShouldWorkForComplicatedObjects()
20+
{
21+
var aHandle = await Page.EvaluateExpressionHandleAsync("window");
22+
Assert.Equal("JSHandle@object", aHandle.ToString());
23+
}
24+
}
25+
}

lib/PuppeteerSharp.Tests/Page/Events/ConsoleTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ void EventHandler(object sender, ConsoleEventArgs e)
2929
Assert.Equal("hello 5 JSHandle@object", message.Text);
3030
Assert.Equal(ConsoleType.Log, message.Type);
3131

32-
Assert.Equal("hello", await message.Args[0].JsonValue());
33-
Assert.Equal(5, await message.Args[1].JsonValue<float>());
34-
Assert.Equal(obj, await message.Args[2].JsonValue<Dictionary<string, object>>());
35-
Assert.Equal("bar", (await message.Args[2].JsonValue<dynamic>()).foo.ToString());
32+
Assert.Equal("hello", await message.Args[0].JsonValueAsync());
33+
Assert.Equal(5, await message.Args[1].JsonValueAsync<float>());
34+
Assert.Equal(obj, await message.Args[2].JsonValueAsync<Dictionary<string, object>>());
35+
Assert.Equal("bar", (await message.Args[2].JsonValueAsync<dynamic>()).foo.ToString());
3636
}
3737

3838
[Fact]

lib/PuppeteerSharp/ExecutionContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ internal async Task<JSHandle> EvaluateFunctionHandleAsync(string script, params
117117
private async Task<T> EvaluateAsync<T>(Task<JSHandle> handleEvaluator)
118118
{
119119
var handle = await handleEvaluator;
120-
var result = await handle.JsonValue<T>()
120+
var result = await handle.JsonValueAsync<T>()
121121
.ContinueWith(jsonTask => jsonTask.Exception != null ? default(T) : jsonTask.Result);
122122

123123
await handle.DisposeAsync();

lib/PuppeteerSharp/JSHandle.cs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,37 @@ public JSHandle(ExecutionContext context, Session client, object remoteObject)
2121
public bool Disposed { get; set; }
2222
public dynamic RemoteObject { get; internal set; }
2323

24-
public Task<Dictionary<string, object>> GetProperty(string propertyName)
24+
/// <summary>
25+
/// Fetches a single property from the referenced object
26+
/// </summary>
27+
/// <param name="propertyName">property to get</param>
28+
/// <returns>Task of <see cref="JSHandle"/></returns>
29+
public async Task<JSHandle> GetPropertyAsync(string propertyName)
2530
{
26-
throw new System.NotImplementedException();
31+
var objectHandle = await ExecutionContext.EvaluateFunctionHandleAsync(@"(object, propertyName) => {
32+
const result = { __proto__: null};
33+
result[propertyName] = object[propertyName];
34+
return result;
35+
}", this, propertyName);
36+
var properties = await objectHandle.GetPropertiesAsync();
37+
properties.TryGetValue(propertyName, out var result);
38+
await objectHandle.DisposeAsync();
39+
return result;
2740
}
2841

42+
/// <summary>
43+
/// Returns a <see cref="Dictionary{TKey, TValue}"/> with property names as keys and <see cref="JSHandle"/> instances for the property values.
44+
/// </summary>
45+
/// <returns>Task which resolves to a <see cref="Dictionary{TKey, TValue}"/></returns>
46+
/// <example>
47+
/// <code>
48+
/// var handle = await page.EvaluateExpressionHandle("({window, document})");
49+
/// var properties = await handle.GetPropertiesAsync();
50+
/// var windowHandle = properties["window"];
51+
/// var documentHandle = properties["document"];
52+
/// await handle.DisposeAsync();
53+
/// </code>
54+
/// </example>
2955
public async Task<Dictionary<string, JSHandle>> GetPropertiesAsync()
3056
{
3157
var response = await _client.SendAsync("Runtime.getProperties", new
@@ -43,9 +69,24 @@ public async Task<Dictionary<string, JSHandle>> GetPropertiesAsync()
4369
return result;
4470
}
4571

46-
public async Task<object> JsonValue() => await JsonValue<object>();
72+
/// <summary>
73+
/// Returns a JSON representation of the object
74+
/// </summary>
75+
/// <returns>Task</returns>
76+
/// <remarks>
77+
/// The method will return an empty JSON if the referenced object is not stringifiable. It will throw an error if the object has circular references
78+
/// </remarks>
79+
public async Task<object> JsonValueAsync() => await JsonValueAsync<object>();
4780

48-
public async Task<T> JsonValue<T>()
81+
/// <summary>
82+
/// Returns a JSON representation of the object
83+
/// </summary>
84+
/// <typeparam name="T">A strongly typed object to parse to</typeparam>
85+
/// <returns>Task</returns>
86+
/// <remarks>
87+
/// The method will return an empty JSON if the referenced object is not stringifiable. It will throw an error if the object has circular references
88+
/// </remarks>
89+
public async Task<T> JsonValueAsync<T>()
4990
{
5091
if (RemoteObject.objectId != null)
5192
{
@@ -83,7 +124,7 @@ public override string ToString()
83124
return "JSHandle@" + type;
84125
}
85126

86-
return Helper.ValueFromRemoteObject<object>(RemoteObject)?.ToString();
127+
return "JSHandle:" + Helper.ValueFromRemoteObject<object>(RemoteObject)?.ToString();
87128
}
88129

89130
internal object FormatArgument(ExecutionContext context)

lib/PuppeteerSharp/Page.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,11 +1217,14 @@ private async Task OnConsoleAPI(PageConsoleResponse message)
12171217
return;
12181218
}
12191219

1220-
var handles = message.Args
1220+
var values = message.Args
12211221
.Select(_ => (JSHandle)_frameManager.CreateJsHandle(message.ExecutionContextId, _))
12221222
.ToList();
1223+
var handles = values
1224+
.ConvertAll(handle => handle.RemoteObject["objectId"] != null
1225+
? handle.ToString() : Helper.ValueFromRemoteObject<object>(handle.RemoteObject));
12231226

1224-
var consoleMessage = new ConsoleMessage(message.Type, string.Join(" ", handles), handles);
1227+
var consoleMessage = new ConsoleMessage(message.Type, string.Join(" ", handles), values);
12251228
Console?.Invoke(this, new ConsoleEventArgs(consoleMessage));
12261229
}
12271230

0 commit comments

Comments
 (0)