Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
112 changes: 83 additions & 29 deletions dotnet/src/webdriver/BiDi/Script/LocalValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,33 @@
// under the License.
// </copyright>

using OpenQA.Selenium.BiDi.Json.Converters;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using OpenQA.Selenium.BiDi.Json.Converters;

namespace OpenQA.Selenium.BiDi.Script;

[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(NumberLocalValue), "number")]
[JsonDerivedType(typeof(StringLocalValue), "string")]
[JsonDerivedType(typeof(NullLocalValue), "null")]
[JsonDerivedType(typeof(UndefinedLocalValue), "undefined")]
[JsonDerivedType(typeof(BooleanLocalValue), "boolean")]
[JsonDerivedType(typeof(BigIntLocalValue), "bigint")]
[JsonDerivedType(typeof(ChannelLocalValue), "channel")]
[JsonDerivedType(typeof(ArrayLocalValue), "array")]
[JsonDerivedType(typeof(DateLocalValue), "date")]
[JsonDerivedType(typeof(MapLocalValue), "map")]
[JsonDerivedType(typeof(ObjectLocalValue), "object")]
[JsonDerivedType(typeof(RegExpLocalValue), "regexp")]
[JsonDerivedType(typeof(SetLocalValue), "set")]
[JsonPolymorphic]
[JsonDerivedType(typeof(SharedReferenceLocalValue))]
[JsonDerivedType(typeof(RemoteObjectReferenceLocalValue))]
[JsonDerivedType(typeof(NumberLocalValue))]
[JsonDerivedType(typeof(StringLocalValue))]
[JsonDerivedType(typeof(NullLocalValue))]
[JsonDerivedType(typeof(UndefinedLocalValue))]
[JsonDerivedType(typeof(BooleanLocalValue))]
[JsonDerivedType(typeof(BigIntLocalValue))]
[JsonDerivedType(typeof(ChannelLocalValue))]
[JsonDerivedType(typeof(ArrayLocalValue))]
[JsonDerivedType(typeof(DateLocalValue))]
[JsonDerivedType(typeof(MapLocalValue))]
[JsonDerivedType(typeof(ObjectLocalValue))]
[JsonDerivedType(typeof(RegExpLocalValue))]
[JsonDerivedType(typeof(SetLocalValue))]
public abstract record LocalValue
{
public static implicit operator LocalValue(bool? value) { return ConvertFrom(value); }
Expand Down Expand Up @@ -279,40 +281,92 @@ private static LocalValue ReflectionBasedConvertFrom(object? value)

return new ObjectLocalValue(values);
}

[JsonInclude]
internal abstract string Type { get; }
}

public abstract record RemoteReferenceLocalValue : LocalValue, IRemoteReference;

public sealed record SharedReferenceLocalValue(string SharedId) : RemoteReferenceLocalValue, ISharedReference
{
public Handle? Handle { get; set; }

internal override string Type { get; } = null!;
}

public sealed record RemoteObjectReferenceLocalValue(Handle Handle) : RemoteReferenceLocalValue, IRemoteObjectReference
{
public string? SharedId { get; set; }

internal override string Type { get; } = null!;
}

public abstract record PrimitiveProtocolLocalValue : LocalValue;

public sealed record NumberLocalValue([property: JsonConverter(typeof(SpecialNumberConverter))] double Value) : PrimitiveProtocolLocalValue
{
internal override string Type { get; } = "number";

public static explicit operator NumberLocalValue(double n) => new NumberLocalValue(n);
}

public sealed record StringLocalValue(string Value) : PrimitiveProtocolLocalValue;
public sealed record StringLocalValue(string Value) : PrimitiveProtocolLocalValue
{
internal override string Type { get; } = "string";
}

public sealed record NullLocalValue : PrimitiveProtocolLocalValue;
public sealed record NullLocalValue : PrimitiveProtocolLocalValue
{
internal override string Type { get; } = "null";
}

public sealed record UndefinedLocalValue : PrimitiveProtocolLocalValue;
public sealed record UndefinedLocalValue : PrimitiveProtocolLocalValue
{
internal override string Type { get; } = "undefined";
}

public sealed record BooleanLocalValue(bool Value) : PrimitiveProtocolLocalValue;
public sealed record BooleanLocalValue(bool Value) : PrimitiveProtocolLocalValue
{
internal override string Type { get; } = "boolean";
}

public sealed record BigIntLocalValue(string Value) : PrimitiveProtocolLocalValue;
public sealed record BigIntLocalValue(string Value) : PrimitiveProtocolLocalValue
{
internal override string Type { get; } = "bigint";
}

public sealed record ChannelLocalValue(ChannelProperties Value) : LocalValue
{
// AddPreloadScript takes arguments typed as ChannelLocalValue but still requires "type":"channel"
[JsonInclude]
internal string Type => "channel";
internal override string Type { get; } = "channel";
}

public sealed record ArrayLocalValue(IEnumerable<LocalValue> Value) : LocalValue;
public sealed record ArrayLocalValue(IEnumerable<LocalValue> Value) : LocalValue
{
internal override string Type { get; } = "array";
}

public sealed record DateLocalValue(string Value) : LocalValue;
public sealed record DateLocalValue(string Value) : LocalValue
{
internal override string Type { get; } = "date";
}

public sealed record MapLocalValue(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue;
public sealed record MapLocalValue(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue
{
internal override string Type { get; } = "map";
}

public sealed record ObjectLocalValue(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue;
public sealed record ObjectLocalValue(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue
{
internal override string Type { get; } = "object";
}

public sealed record RegExpLocalValue(RegExpValue Value) : LocalValue;
public sealed record RegExpLocalValue(RegExpValue Value) : LocalValue
{
internal override string Type { get; } = "regexp";
}

public sealed record SetLocalValue(IEnumerable<LocalValue> Value) : LocalValue;
public sealed record SetLocalValue(IEnumerable<LocalValue> Value) : LocalValue
{
internal override string Type { get; } = "set";
}
6 changes: 4 additions & 2 deletions dotnet/src/webdriver/BiDi/Script/RemoteValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
using OpenQA.Selenium.BiDi.Json.Converters.Polymorphic;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace OpenQA.Selenium.BiDi.Script;
Expand Down Expand Up @@ -78,6 +76,10 @@ public abstract record RemoteValue
{
var type = typeof(TResult);

if (typeof(RemoteValue).IsAssignableFrom(type)) // handle native derived types
{
return (TResult)(this as object);
}
if (type == typeof(bool))
{
return (TResult)(Convert.ToBoolean(((BooleanRemoteValue)this).Value) as object);
Expand Down
42 changes: 42 additions & 0 deletions dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -363,4 +363,46 @@ public async Task CanCallFunctionWithArgumentSet()

Assert.That(result, Is.TypeOf<EvaluateResultSuccess>(), $"Call was not successful: {result}");
}

[Test]
public async Task CanCallFunctionWithSharedReferenceLocalValue()
{
// Navigate to a page with a known element
driver.Url = UrlBuilder.WhereIs("bidi/logEntryAdded.html");

var node = (await context.LocateNodesAsync(new BrowsingContext.CssLocator("#consoleLog"))).Nodes[0];

var arg = new SharedReferenceLocalValue(node.SharedId);

var result = await context.Script.CallFunctionAsync($$"""
(el) => {
if (!(el instanceof Element) || el.id !== 'consoleLog') {
throw new Error("Assert failed: " + (el && el.id));
}
}
""", false, new() { Arguments = [arg] });

Assert.That(result, Is.TypeOf<EvaluateResultSuccess>(), $"Call was not successful: {result}");
}

[Test]
public async Task CanCallFunctionWithRemoteObjectReferenceLocalValue()
{
ObjectRemoteValue objectRemoteValue = await context.Script.CallFunctionAsync<ObjectRemoteValue>(
"() => ({ a: 42 })",
true,
new() { ResultOwnership = ResultOwnership.Root });

var arg = new RemoteObjectReferenceLocalValue(objectRemoteValue.Handle!);

var result = await context.Script.CallFunctionAsync($$"""
(refObj) => {
if (typeof refObj !== 'object' || refObj.a !== 42) {
throw new Error("Assert failed: ref a=" + (refObj && refObj.a));
}
}
""", false, new() { Arguments = [arg] });

Assert.That(result, Is.TypeOf<EvaluateResultSuccess>(), $"Call was not successful: {result}");
}
}