Skip to content

Commit 3d5eaec

Browse files
authored
Add missing JSHandle tests (#1815)
closes #1740
1 parent 6b50007 commit 3d5eaec

File tree

5 files changed

+169
-18
lines changed

5 files changed

+169
-18
lines changed

lib/PuppeteerSharp.Tests/JSHandleTests/JsonValueTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,24 @@ public async Task ShouldWork()
2323
Assert.Equal(JObject.Parse("{ foo: 'bar' }"), json);
2424
}
2525

26+
[PuppeteerTest("jshandle.spec.ts", "JSHandle.jsonValue", "works with jsonValues that are not objects")]
27+
[SkipBrowserFact(skipFirefox: true)]
28+
public async Task WorksWithJsonValuesThatAreNotObjects()
29+
{
30+
var aHandle = await Page.EvaluateFunctionHandleAsync("() => ['a', 'b']");
31+
var json = await aHandle.JsonValueAsync<string[]>();
32+
Assert.Equal(new[] {"a","b" }, json);
33+
}
34+
35+
[PuppeteerTest("jshandle.spec.ts", "JSHandle.jsonValue", "works with jsonValues that are primitives")]
36+
[SkipBrowserFact(skipFirefox: true)]
37+
public async Task WorksWithJsonValuesThatArePrimitives()
38+
{
39+
var aHandle = await Page.EvaluateFunctionHandleAsync("() => 'foo'");
40+
var json = await aHandle.JsonValueAsync<string>();
41+
Assert.Equal("foo", json);
42+
}
43+
2644
[PuppeteerTest("jshandle.spec.ts", "JSHandle.jsonValue", "should not work with dates")]
2745
[SkipBrowserFact(skipFirefox: true)]
2846
public async Task ShouldNotWorkWithDates()
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using System.Threading.Tasks;
2+
using PuppeteerSharp.Xunit;
3+
using Xunit;
4+
using Xunit.Abstractions;
5+
6+
namespace PuppeteerSharp.Tests.JSHandleTests
7+
{
8+
[Collection(TestConstants.TestFixtureCollectionName)]
9+
public class PageEvaluateHandle : PuppeteerPageBaseTest
10+
{
11+
public PageEvaluateHandle(ITestOutputHelper output) : base(output)
12+
{
13+
}
14+
15+
[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should work")]
16+
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
17+
public async Task ShouldWork()
18+
=> Assert.NotNull(await Page.EvaluateFunctionHandleAsync("() => window"));
19+
20+
[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should accept object handle as an argument")]
21+
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
22+
public async Task ShouldAcceptObjectHandleAsAnArgument()
23+
{
24+
var navigatorHandle = await Page.EvaluateFunctionHandleAsync("() => navigator");
25+
var text = await Page.EvaluateFunctionAsync<string>(
26+
"(e) => e.userAgent",
27+
navigatorHandle);
28+
Assert.Contains("Mozilla", text);
29+
}
30+
31+
[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should accept object handle to primitive types")]
32+
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
33+
public async Task ShouldAcceptObjectHandleToPrimitiveTypes()
34+
{
35+
var aHandle = await Page.EvaluateFunctionHandleAsync("() => 5");
36+
var isFive = await Page.EvaluateFunctionAsync<bool>(
37+
"(e) => Object.is(e, 5)",
38+
aHandle);
39+
Assert.True(isFive);
40+
}
41+
42+
[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should warn on nested object handles")]
43+
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
44+
public async Task ShouldWarnOnNestedObjectHandles()
45+
{
46+
var aHandle = await Page.EvaluateFunctionHandleAsync("() => document.body");
47+
var exception = await Assert.ThrowsAsync<EvaluationFailedException>(() =>
48+
Page.EvaluateFunctionHandleAsync("(opts) => opts.elem.querySelector('p')", new { aHandle }));
49+
Assert.Contains("Are you passing a nested JSHandle?", exception.Message);
50+
}
51+
52+
[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should accept object handle to unserializable value")]
53+
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
54+
public async Task ShouldAcceptObjectHandleToUnserializableValue()
55+
{
56+
var aHandle = await Page.EvaluateFunctionHandleAsync("() => Infinity");
57+
Assert.True(await Page.EvaluateFunctionAsync<bool>(
58+
"(e) => Object.is(e, Infinity)",
59+
aHandle));
60+
}
61+
62+
[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should use the same JS wrappers")]
63+
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
64+
public async Task ShouldUseTheSameJSWrappers()
65+
{
66+
var aHandle = await Page.EvaluateFunctionHandleAsync(@"() => {
67+
globalThis.FOO = 123;
68+
return window;
69+
}");
70+
Assert.Equal(123, await Page.EvaluateFunctionAsync<int>(
71+
"(e) => e.FOO",
72+
aHandle));
73+
}
74+
}
75+
}

lib/PuppeteerSharp.Tests/JSHandleTests/ToStringTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,29 @@ public async Task ShouldWorkForComplicatedObjects()
2929
var aHandle = await Page.EvaluateExpressionHandleAsync("window");
3030
Assert.Equal("JSHandle@object", aHandle.ToString());
3131
}
32+
33+
[PuppeteerTest("jshandle.spec.ts", "JSHandle.toString", "should work with different subtypes")]
34+
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
35+
public async Task ShouldWorkWithDifferentSubtypes()
36+
{
37+
Assert.Equal("JSHandle@function", (await Page.EvaluateExpressionHandleAsync("(function(){})")).ToString());
38+
Assert.Equal("JSHandle:12", (await Page.EvaluateExpressionHandleAsync("12")).ToString());
39+
Assert.Equal("JSHandle:True", (await Page.EvaluateExpressionHandleAsync("true")).ToString());
40+
Assert.Equal("JSHandle:undefined", (await Page.EvaluateExpressionHandleAsync("undefined")).ToString());
41+
Assert.Equal("JSHandle:foo", (await Page.EvaluateExpressionHandleAsync("'foo'")).ToString());
42+
Assert.Equal("JSHandle@symbol", (await Page.EvaluateExpressionHandleAsync("Symbol()")).ToString());
43+
Assert.Equal("JSHandle@map", (await Page.EvaluateExpressionHandleAsync("new Map()")).ToString());
44+
Assert.Equal("JSHandle@set", (await Page.EvaluateExpressionHandleAsync("new Set()")).ToString());
45+
Assert.Equal("JSHandle@array", (await Page.EvaluateExpressionHandleAsync("[]")).ToString());
46+
Assert.Equal("JSHandle:null", (await Page.EvaluateExpressionHandleAsync("null")).ToString());
47+
Assert.Equal("JSHandle@regexp", (await Page.EvaluateExpressionHandleAsync("/foo/")).ToString());
48+
Assert.Equal("JSHandle@node", (await Page.EvaluateExpressionHandleAsync("document.body")).ToString());
49+
Assert.Equal("JSHandle@date", (await Page.EvaluateExpressionHandleAsync("new Date()")).ToString());
50+
Assert.Equal("JSHandle@weakmap", (await Page.EvaluateExpressionHandleAsync("new WeakMap()")).ToString());
51+
Assert.Equal("JSHandle@weakset", (await Page.EvaluateExpressionHandleAsync("new WeakSet()")).ToString());
52+
Assert.Equal("JSHandle@error", (await Page.EvaluateExpressionHandleAsync("new Error()")).ToString());
53+
Assert.Equal("JSHandle@typedarray", (await Page.EvaluateExpressionHandleAsync("new Int32Array()")).ToString());
54+
Assert.Equal("JSHandle@proxy", (await Page.EvaluateExpressionHandleAsync("new Proxy({}, {})")).ToString());
55+
}
3256
}
3357
}

lib/PuppeteerSharp/Helpers/RemoteObjectHelper.cs

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace PuppeteerSharp.Helpers
1010
{
1111
internal class RemoteObjectHelper
1212
{
13-
internal static object ValueFromRemoteObject<T>(RemoteObject remoteObject)
13+
internal static object ValueFromRemoteObject<T>(RemoteObject remoteObject, bool stringify = false)
1414
{
1515
var unserializableValue = remoteObject.UnserializableValue;
1616

@@ -19,32 +19,66 @@ internal static object ValueFromRemoteObject<T>(RemoteObject remoteObject)
1919
return ValueFromUnserializableValue(remoteObject, unserializableValue);
2020
}
2121

22+
if (stringify)
23+
{
24+
if (remoteObject.Type == RemoteObjectType.Undefined)
25+
{
26+
return "undefined";
27+
}
28+
29+
if (remoteObject.Value == null)
30+
{
31+
return "null";
32+
}
33+
}
34+
2235
var value = remoteObject.Value;
2336

2437
if (value == null)
2538
{
2639
return default(T);
2740
}
2841

29-
return typeof(T) == typeof(JToken) ? value : ValueFromType<T>(value, remoteObject.Type);
42+
return typeof(T) == typeof(JToken) ? value : ValueFromType<T>(value, remoteObject.Type, stringify);
3043
}
3144

32-
private static object ValueFromType<T>(JToken value, RemoteObjectType objectType)
45+
private static object ValueFromType<T>(JToken value, RemoteObjectType objectType, bool stringify = false)
3346
{
34-
switch (objectType)
47+
if (stringify)
3548
{
36-
case RemoteObjectType.Object:
37-
return value.ToObject<T>(true);
38-
case RemoteObjectType.Undefined:
39-
return null;
40-
case RemoteObjectType.Number:
41-
return value.Value<T>();
42-
case RemoteObjectType.Boolean:
43-
return value.Value<bool>();
44-
case RemoteObjectType.Bigint:
45-
return value.Value<double>();
46-
default: // string, symbol, function
47-
return value.ToObject<T>();
49+
switch (objectType)
50+
{
51+
case RemoteObjectType.Object:
52+
return value.ToObject<T>(true);
53+
case RemoteObjectType.Undefined:
54+
return "undefined";
55+
case RemoteObjectType.Number:
56+
return value.Value<T>();
57+
case RemoteObjectType.Boolean:
58+
return value.Value<bool>();
59+
case RemoteObjectType.Bigint:
60+
return value.Value<double>();
61+
default: // string, symbol, function
62+
return value.ToObject<T>();
63+
}
64+
}
65+
else
66+
{
67+
switch (objectType)
68+
{
69+
case RemoteObjectType.Object:
70+
return value.ToObject<T>(true);
71+
case RemoteObjectType.Undefined:
72+
return null;
73+
case RemoteObjectType.Number:
74+
return value.Value<T>();
75+
case RemoteObjectType.Boolean:
76+
return value.Value<bool>();
77+
case RemoteObjectType.Bigint:
78+
return value.Value<double>();
79+
default: // string, symbol, function
80+
return value.ToObject<T>();
81+
}
4882
}
4983
}
5084

lib/PuppeteerSharp/JSHandle.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ public override string ToString()
164164
return "JSHandle@" + type.ToLower(System.Globalization.CultureInfo.CurrentCulture);
165165
}
166166

167-
return "JSHandle:" + RemoteObjectHelper.ValueFromRemoteObject<object>(RemoteObject)?.ToString();
167+
return "JSHandle:" + RemoteObjectHelper.ValueFromRemoteObject<object>(RemoteObject, true)?.ToString();
168168
}
169169

170170
/// <summary>
@@ -235,7 +235,7 @@ internal object FormatArgument(ExecutionContext context)
235235

236236
if (unserializableValue != null)
237237
{
238-
return unserializableValue;
238+
return new { unserializableValue };
239239
}
240240

241241
if (RemoteObject.ObjectId == null)

0 commit comments

Comments
 (0)