Skip to content

Commit

Permalink
Add missing JSHandle tests (#1815)
Browse files Browse the repository at this point in the history
closes #1740
  • Loading branch information
kblok authored Sep 27, 2021
1 parent 6b50007 commit 3d5eaec
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 18 deletions.
18 changes: 18 additions & 0 deletions lib/PuppeteerSharp.Tests/JSHandleTests/JsonValueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ public async Task ShouldWork()
Assert.Equal(JObject.Parse("{ foo: 'bar' }"), json);
}

[PuppeteerTest("jshandle.spec.ts", "JSHandle.jsonValue", "works with jsonValues that are not objects")]
[SkipBrowserFact(skipFirefox: true)]
public async Task WorksWithJsonValuesThatAreNotObjects()
{
var aHandle = await Page.EvaluateFunctionHandleAsync("() => ['a', 'b']");
var json = await aHandle.JsonValueAsync<string[]>();
Assert.Equal(new[] {"a","b" }, json);
}

[PuppeteerTest("jshandle.spec.ts", "JSHandle.jsonValue", "works with jsonValues that are primitives")]
[SkipBrowserFact(skipFirefox: true)]
public async Task WorksWithJsonValuesThatArePrimitives()
{
var aHandle = await Page.EvaluateFunctionHandleAsync("() => 'foo'");
var json = await aHandle.JsonValueAsync<string>();
Assert.Equal("foo", json);
}

[PuppeteerTest("jshandle.spec.ts", "JSHandle.jsonValue", "should not work with dates")]
[SkipBrowserFact(skipFirefox: true)]
public async Task ShouldNotWorkWithDates()
Expand Down
75 changes: 75 additions & 0 deletions lib/PuppeteerSharp.Tests/JSHandleTests/PageEvaluateHandle.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Threading.Tasks;
using PuppeteerSharp.Xunit;
using Xunit;
using Xunit.Abstractions;

namespace PuppeteerSharp.Tests.JSHandleTests
{
[Collection(TestConstants.TestFixtureCollectionName)]
public class PageEvaluateHandle : PuppeteerPageBaseTest
{
public PageEvaluateHandle(ITestOutputHelper output) : base(output)
{
}

[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should work")]
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
public async Task ShouldWork()
=> Assert.NotNull(await Page.EvaluateFunctionHandleAsync("() => window"));

[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should accept object handle as an argument")]
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
public async Task ShouldAcceptObjectHandleAsAnArgument()
{
var navigatorHandle = await Page.EvaluateFunctionHandleAsync("() => navigator");
var text = await Page.EvaluateFunctionAsync<string>(
"(e) => e.userAgent",
navigatorHandle);
Assert.Contains("Mozilla", text);
}

[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should accept object handle to primitive types")]
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
public async Task ShouldAcceptObjectHandleToPrimitiveTypes()
{
var aHandle = await Page.EvaluateFunctionHandleAsync("() => 5");
var isFive = await Page.EvaluateFunctionAsync<bool>(
"(e) => Object.is(e, 5)",
aHandle);
Assert.True(isFive);
}

[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should warn on nested object handles")]
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
public async Task ShouldWarnOnNestedObjectHandles()
{
var aHandle = await Page.EvaluateFunctionHandleAsync("() => document.body");
var exception = await Assert.ThrowsAsync<EvaluationFailedException>(() =>
Page.EvaluateFunctionHandleAsync("(opts) => opts.elem.querySelector('p')", new { aHandle }));
Assert.Contains("Are you passing a nested JSHandle?", exception.Message);
}

[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should accept object handle to unserializable value")]
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
public async Task ShouldAcceptObjectHandleToUnserializableValue()
{
var aHandle = await Page.EvaluateFunctionHandleAsync("() => Infinity");
Assert.True(await Page.EvaluateFunctionAsync<bool>(
"(e) => Object.is(e, Infinity)",
aHandle));
}

[PuppeteerTest("jshandle.spec.ts", "Page.evaluateHandle", "should use the same JS wrappers")]
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
public async Task ShouldUseTheSameJSWrappers()
{
var aHandle = await Page.EvaluateFunctionHandleAsync(@"() => {
globalThis.FOO = 123;
return window;
}");
Assert.Equal(123, await Page.EvaluateFunctionAsync<int>(
"(e) => e.FOO",
aHandle));
}
}
}
24 changes: 24 additions & 0 deletions lib/PuppeteerSharp.Tests/JSHandleTests/ToStringTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,29 @@ public async Task ShouldWorkForComplicatedObjects()
var aHandle = await Page.EvaluateExpressionHandleAsync("window");
Assert.Equal("JSHandle@object", aHandle.ToString());
}

[PuppeteerTest("jshandle.spec.ts", "JSHandle.toString", "should work with different subtypes")]
[Fact(Timeout = TestConstants.DefaultTestTimeout)]
public async Task ShouldWorkWithDifferentSubtypes()
{
Assert.Equal("JSHandle@function", (await Page.EvaluateExpressionHandleAsync("(function(){})")).ToString());
Assert.Equal("JSHandle:12", (await Page.EvaluateExpressionHandleAsync("12")).ToString());
Assert.Equal("JSHandle:True", (await Page.EvaluateExpressionHandleAsync("true")).ToString());
Assert.Equal("JSHandle:undefined", (await Page.EvaluateExpressionHandleAsync("undefined")).ToString());
Assert.Equal("JSHandle:foo", (await Page.EvaluateExpressionHandleAsync("'foo'")).ToString());
Assert.Equal("JSHandle@symbol", (await Page.EvaluateExpressionHandleAsync("Symbol()")).ToString());
Assert.Equal("JSHandle@map", (await Page.EvaluateExpressionHandleAsync("new Map()")).ToString());
Assert.Equal("JSHandle@set", (await Page.EvaluateExpressionHandleAsync("new Set()")).ToString());
Assert.Equal("JSHandle@array", (await Page.EvaluateExpressionHandleAsync("[]")).ToString());
Assert.Equal("JSHandle:null", (await Page.EvaluateExpressionHandleAsync("null")).ToString());
Assert.Equal("JSHandle@regexp", (await Page.EvaluateExpressionHandleAsync("/foo/")).ToString());
Assert.Equal("JSHandle@node", (await Page.EvaluateExpressionHandleAsync("document.body")).ToString());
Assert.Equal("JSHandle@date", (await Page.EvaluateExpressionHandleAsync("new Date()")).ToString());
Assert.Equal("JSHandle@weakmap", (await Page.EvaluateExpressionHandleAsync("new WeakMap()")).ToString());
Assert.Equal("JSHandle@weakset", (await Page.EvaluateExpressionHandleAsync("new WeakSet()")).ToString());
Assert.Equal("JSHandle@error", (await Page.EvaluateExpressionHandleAsync("new Error()")).ToString());
Assert.Equal("JSHandle@typedarray", (await Page.EvaluateExpressionHandleAsync("new Int32Array()")).ToString());
Assert.Equal("JSHandle@proxy", (await Page.EvaluateExpressionHandleAsync("new Proxy({}, {})")).ToString());
}
}
}
66 changes: 50 additions & 16 deletions lib/PuppeteerSharp/Helpers/RemoteObjectHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace PuppeteerSharp.Helpers
{
internal class RemoteObjectHelper
{
internal static object ValueFromRemoteObject<T>(RemoteObject remoteObject)
internal static object ValueFromRemoteObject<T>(RemoteObject remoteObject, bool stringify = false)
{
var unserializableValue = remoteObject.UnserializableValue;

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

if (stringify)
{
if (remoteObject.Type == RemoteObjectType.Undefined)
{
return "undefined";
}

if (remoteObject.Value == null)
{
return "null";
}
}

var value = remoteObject.Value;

if (value == null)
{
return default(T);
}

return typeof(T) == typeof(JToken) ? value : ValueFromType<T>(value, remoteObject.Type);
return typeof(T) == typeof(JToken) ? value : ValueFromType<T>(value, remoteObject.Type, stringify);
}

private static object ValueFromType<T>(JToken value, RemoteObjectType objectType)
private static object ValueFromType<T>(JToken value, RemoteObjectType objectType, bool stringify = false)
{
switch (objectType)
if (stringify)
{
case RemoteObjectType.Object:
return value.ToObject<T>(true);
case RemoteObjectType.Undefined:
return null;
case RemoteObjectType.Number:
return value.Value<T>();
case RemoteObjectType.Boolean:
return value.Value<bool>();
case RemoteObjectType.Bigint:
return value.Value<double>();
default: // string, symbol, function
return value.ToObject<T>();
switch (objectType)
{
case RemoteObjectType.Object:
return value.ToObject<T>(true);
case RemoteObjectType.Undefined:
return "undefined";
case RemoteObjectType.Number:
return value.Value<T>();
case RemoteObjectType.Boolean:
return value.Value<bool>();
case RemoteObjectType.Bigint:
return value.Value<double>();
default: // string, symbol, function
return value.ToObject<T>();
}
}
else
{
switch (objectType)
{
case RemoteObjectType.Object:
return value.ToObject<T>(true);
case RemoteObjectType.Undefined:
return null;
case RemoteObjectType.Number:
return value.Value<T>();
case RemoteObjectType.Boolean:
return value.Value<bool>();
case RemoteObjectType.Bigint:
return value.Value<double>();
default: // string, symbol, function
return value.ToObject<T>();
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions lib/PuppeteerSharp/JSHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public override string ToString()
return "JSHandle@" + type.ToLower(System.Globalization.CultureInfo.CurrentCulture);
}

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

/// <summary>
Expand Down Expand Up @@ -235,7 +235,7 @@ internal object FormatArgument(ExecutionContext context)

if (unserializableValue != null)
{
return unserializableValue;
return new { unserializableValue };
}

if (RemoteObject.ObjectId == null)
Expand Down

0 comments on commit 3d5eaec

Please sign in to comment.