Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dotnet] [bidi] Simplify conversion to LocalValue #15441

Merged
merged 21 commits into from
Mar 27, 2025
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ccc536d
[dotnet] [bidi] Simplify usage of `LocalValue`
RenderMichael Mar 17, 2025
0722af5
Update exception
RenderMichael Mar 17, 2025
cd55951
Use new methods better in tests
RenderMichael Mar 17, 2025
47891c5
`String(null)` returns `NullLocalValue`
RenderMichael Mar 17, 2025
d367a66
Merge branch 'trunk' into local-value
RenderMichael Mar 17, 2025
c7cbaf7
Add regex overload that takes a string pattern
RenderMichael Mar 17, 2025
0c4f8bf
Remote static factory methods for now
RenderMichael Mar 21, 2025
6ae7a94
remove unnecessary change
RenderMichael Mar 21, 2025
c7ac3b9
Avoid BigInt until we need it
RenderMichael Mar 21, 2025
c73fd33
Account for BigInt in ConvertFrom(object)
RenderMichael Mar 21, 2025
a762425
Avoid implicit casts in tests that aren't there to test it
RenderMichael Mar 21, 2025
02ace40
Merge branch 'trunk' into local-value
RenderMichael Mar 21, 2025
2ae4fad
Remote `LocalValue.ConvertFrom(JsonNode)`, expand `ConvertFrom(object)`
RenderMichael Mar 25, 2025
49f1347
Merge branch 'trunk' into local-value
RenderMichael Mar 25, 2025
e875b05
Add ConvertFrom support for `DateTime` and `long`
RenderMichael Mar 25, 2025
4e6db49
Add unit tests to LocalValue operators
RenderMichael Mar 25, 2025
e1ce7ed
Merge branch 'trunk' into local-value
RenderMichael Mar 25, 2025
efd0058
Use var
RenderMichael Mar 25, 2025
64c5eb0
Use in-line literals for `LocalValue` conversions, use a separate fix…
RenderMichael Mar 26, 2025
e647628
Merge branch 'trunk' into local-value
RenderMichael Mar 26, 2025
d0da6e2
Use int literal
RenderMichael Mar 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 80 additions & 7 deletions dotnet/src/webdriver/BiDi/Modules/Script/LocalValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@
// under the License.
// </copyright>

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace OpenQA.Selenium.BiDi.Modules.Script;

Expand All @@ -38,22 +44,40 @@ namespace OpenQA.Selenium.BiDi.Modules.Script;
[JsonDerivedType(typeof(SetLocalValue), "set")]
public abstract record LocalValue
{
public static implicit operator LocalValue(int value) { return new NumberLocalValue(value); }
public static implicit operator LocalValue(bool? value) { return value is bool b ? new BooleanLocalValue(b) : new NullLocalValue(); }
public static implicit operator LocalValue(int? value) { return value is int i ? new NumberLocalValue(i) : new NullLocalValue(); }
public static implicit operator LocalValue(double? value) { return value is double d ? new NumberLocalValue(d) : new NullLocalValue(); }
public static implicit operator LocalValue(string? value) { return value is null ? new NullLocalValue() : new StringLocalValue(value); }

// TODO: Extend converting from types
public static LocalValue ConvertFrom(object? value)
{
switch (value)
{
case LocalValue:
return (LocalValue)value;
case LocalValue localValue:
return localValue;

case null:
return new NullLocalValue();
case int:
return (int)value;
case string:
return (string)value;

case bool b:
return new BooleanLocalValue(b);

case int i:
return new NumberLocalValue(i);

case double d:
return new NumberLocalValue(d);

case BigInteger bigInt:
return new BigIntLocalValue(bigInt.ToString());

case string str:
return new StringLocalValue(str);

case IEnumerable<object?> list:
return new ArrayLocalValue(list.Select(ConvertFrom).ToList());

case object:
{
var type = value.GetType();
Expand All @@ -71,6 +95,55 @@ public static LocalValue ConvertFrom(object? value)
}
}
}

public static LocalValue ConvertFrom(JsonNode? node)
{
if (node is null)
{
return new NullLocalValue();
}

switch (node.GetValueKind())
{
case System.Text.Json.JsonValueKind.Null:
return new NullLocalValue();

case System.Text.Json.JsonValueKind.True:
return new BooleanLocalValue(true);

case System.Text.Json.JsonValueKind.False:
return new BooleanLocalValue(false);

case System.Text.Json.JsonValueKind.String:
return new StringLocalValue(node.ToString());

case System.Text.Json.JsonValueKind.Number:
{
var numberString = node.ToString();

var numberAsDouble = double.Parse(numberString);

if (double.IsInfinity(numberAsDouble))
{
// Numbers outside of Int64's range will successfully parse, but become +- Infinity
// We can retain the value using a BigInt
return new BigIntLocalValue(numberString);
}

return new NumberLocalValue(numberAsDouble);
}

case System.Text.Json.JsonValueKind.Array:
return new ArrayLocalValue(node.AsArray().Select(ConvertFrom));

case System.Text.Json.JsonValueKind.Object:
var convertedToListForm = node.AsObject().Select(property => new LocalValue[] { new StringLocalValue(property.Key), ConvertFrom(property.Value) }).ToList();
return new ObjectLocalValue(convertedToListForm);

default:
throw new InvalidOperationException("Invalid JSON node");
}
}
}

public abstract record PrimitiveProtocolLocalValue : LocalValue;
Expand Down
18 changes: 17 additions & 1 deletion dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ await context.Script.CallFunctionAsync($$"""
}

[Test]
public void CanCallFunctionWithArgumentBoolean()
public void CanCallFunctionWithArgumentTrue()
{
var arg = new BooleanLocalValue(true);
Assert.That(async () =>
Expand All @@ -72,6 +72,22 @@ await context.Script.CallFunctionAsync($$"""
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentFalse()
{
var arg = new BooleanLocalValue(false);
Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg !== false) {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [arg] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentBigInt()
{
Expand Down