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] Make LocalValue types not nested #15428

Merged
merged 5 commits into from
Mar 16, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,6 @@ namespace OpenQA.Selenium.BiDi.Communication.Json;
[JsonSerializable(typeof(Modules.Network.FetchErrorEventArgs))]
[JsonSerializable(typeof(Modules.Network.AuthRequiredEventArgs))]

[JsonSerializable(typeof(Modules.Script.Channel), TypeInfoPropertyName = "Script_Channel")]
[JsonSerializable(typeof(Modules.Script.LocalValue.String), TypeInfoPropertyName = "Script_LocalValue_String")]
[JsonSerializable(typeof(Modules.Script.Target.Realm), TypeInfoPropertyName = "Script_Target_Realm")]
[JsonSerializable(typeof(Modules.Script.Target.Context), TypeInfoPropertyName = "Script_Target_Context")]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace OpenQA.Selenium.BiDi.Modules.Script;
internal class AddPreloadScriptCommand(AddPreloadScriptCommandParameters @params)
: Command<AddPreloadScriptCommandParameters>(@params, "script.addPreloadScript");

internal record AddPreloadScriptCommandParameters(string FunctionDeclaration, IEnumerable<LocalValue.Channel>? Arguments, IEnumerable<BrowsingContext.BrowsingContext>? Contexts, string? Sandbox) : CommandParameters;
internal record AddPreloadScriptCommandParameters(string FunctionDeclaration, IEnumerable<ChannelLocalValue>? Arguments, IEnumerable<BrowsingContext.BrowsingContext>? Contexts, string? Sandbox) : CommandParameters;

public record AddPreloadScriptOptions : CommandOptions
{
Expand All @@ -37,7 +37,7 @@ internal AddPreloadScriptOptions(BrowsingContextAddPreloadScriptOptions? options
Sandbox = options?.Sandbox;
}

public IEnumerable<LocalValue.Channel>? Arguments { get; set; }
public IEnumerable<ChannelLocalValue>? Arguments { get; set; }

public IEnumerable<BrowsingContext.BrowsingContext>? Contexts { get; set; }

Expand All @@ -46,7 +46,7 @@ internal AddPreloadScriptOptions(BrowsingContextAddPreloadScriptOptions? options

public record BrowsingContextAddPreloadScriptOptions
{
public IEnumerable<LocalValue.Channel>? Arguments { get; set; }
public IEnumerable<ChannelLocalValue>? Arguments { get; set; }

public string? Sandbox { get; set; }
}
Expand Down
93 changes: 50 additions & 43 deletions dotnet/src/webdriver/BiDi/Modules/Script/LocalValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,23 @@
namespace OpenQA.Selenium.BiDi.Modules.Script;

[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(Number), "number")]
[JsonDerivedType(typeof(String), "string")]
[JsonDerivedType(typeof(Null), "null")]
[JsonDerivedType(typeof(Undefined), "undefined")]
[JsonDerivedType(typeof(Channel), "channel")]
[JsonDerivedType(typeof(Array), "array")]
[JsonDerivedType(typeof(Date), "date")]
[JsonDerivedType(typeof(Map), "map")]
[JsonDerivedType(typeof(Object), "object")]
[JsonDerivedType(typeof(RegExp), "regexp")]
[JsonDerivedType(typeof(Set), "set")]
[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")]
public abstract record LocalValue
{
public static implicit operator LocalValue(int value) { return new Number(value); }
public static implicit operator LocalValue(string value) { return new String(value); }
public static implicit operator LocalValue(int value) { return new NumberLocalValue(value); }
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)
Expand All @@ -47,7 +49,7 @@ public static LocalValue ConvertFrom(object? value)
case LocalValue:
return (LocalValue)value;
case null:
return new Null();
return new NullLocalValue();
case int:
return (int)value;
case string:
Expand All @@ -65,52 +67,57 @@ public static LocalValue ConvertFrom(object? value)
values.Add([property.Name, ConvertFrom(property.GetValue(value))]);
}

return new Object(values);
return new ObjectLocalValue(values);
}
}
}
}

public abstract record PrimitiveProtocolLocalValue : LocalValue;
public abstract record PrimitiveProtocolLocalValue : LocalValue;

public record Number(double Value) : PrimitiveProtocolLocalValue
{
public static explicit operator Number(double n) => new Number(n);
}
public record NumberLocalValue(double Value) : PrimitiveProtocolLocalValue
{
public static explicit operator NumberLocalValue(double n) => new NumberLocalValue(n);
}

public record String(string Value) : PrimitiveProtocolLocalValue;
public record StringLocalValue(string Value) : PrimitiveProtocolLocalValue;

public record Null : PrimitiveProtocolLocalValue;
public record NullLocalValue : PrimitiveProtocolLocalValue;

public record Undefined : PrimitiveProtocolLocalValue;
public record UndefinedLocalValue : PrimitiveProtocolLocalValue;

public record Channel(Channel.ChannelProperties Value) : LocalValue
{
[JsonInclude]
internal string type = "channel";
public record BooleanLocalValue(bool Value) : PrimitiveProtocolLocalValue;

public record ChannelProperties(Script.Channel Channel)
{
public SerializationOptions? SerializationOptions { get; set; }
public record BigIntLocalValue(string Value) : PrimitiveProtocolLocalValue;

public ResultOwnership? Ownership { get; set; }
}
public record ChannelLocalValue(ChannelLocalValue.ChannelProperties Value) : LocalValue
{
// TODO: Revise why we need it
[JsonInclude]
internal string type = "channel";

public record ChannelProperties(Channel Channel)
{
public SerializationOptions? SerializationOptions { get; set; }

public ResultOwnership? Ownership { get; set; }
}
}

public record Array(IEnumerable<LocalValue> Value) : LocalValue;
public record ArrayLocalValue(IEnumerable<LocalValue> Value) : LocalValue;

public record Date(string Value) : LocalValue;
public record DateLocalValue(string Value) : LocalValue;

public record Map(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue;
public record MapLocalValue(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue;

public record Object(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue;
public record ObjectLocalValue(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue;

public record RegExp(RegExp.RegExpValue Value) : LocalValue
public record RegExpLocalValue(RegExpLocalValue.RegExpValue Value) : LocalValue
{
public record RegExpValue(string Pattern)
{
public record RegExpValue(string Pattern)
{
public string? Flags { get; set; }
}
public string? Flags { get; set; }
}

public record Set(IEnumerable<LocalValue> Value) : LocalValue;
}

public record SetLocalValue(IEnumerable<LocalValue> Value) : LocalValue;
68 changes: 50 additions & 18 deletions dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class CallFunctionLocalValueTest : BiDiTestFixture
[Test]
public void CanCallFunctionWithArgumentUndefined()
{
var arg = new LocalValue.Undefined();
var arg = new UndefinedLocalValue();
Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
Expand All @@ -43,7 +43,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentNull()
{
var arg = new LocalValue.Null();
var arg = new NullLocalValue();
Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
Expand All @@ -56,10 +56,42 @@ await context.Script.CallFunctionAsync($$"""
}, Throws.Nothing);
}

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

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

[Test]
public void CanCallFunctionWithArgumentEmptyString()
{
var arg = new LocalValue.String(string.Empty);
var arg = new StringLocalValue(string.Empty);
Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
Expand All @@ -75,7 +107,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentNonEmptyString()
{
var arg = new LocalValue.String("whoa");
var arg = new StringLocalValue("whoa");
Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
Expand All @@ -93,7 +125,7 @@ public void CanCallFunctionWithArgumentRecentDate()
{
const string PinnedDateTimeString = "2025-03-09T00:30:33.083Z";

var arg = new LocalValue.Date(PinnedDateTimeString);
var arg = new DateLocalValue(PinnedDateTimeString);

Assert.That(async () =>
{
Expand All @@ -112,7 +144,7 @@ public void CanCallFunctionWithArgumentEpochDate()
{
const string EpochString = "1970-01-01T00:00:00.000Z";

var arg = new LocalValue.Date(EpochString);
var arg = new DateLocalValue(EpochString);

Assert.That(async () =>
{
Expand All @@ -129,7 +161,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentNumberFive()
{
var arg = new LocalValue.Number(5);
var arg = new NumberLocalValue(5);

Assert.That(async () =>
{
Expand All @@ -146,7 +178,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentNumberNegativeFive()
{
var arg = new LocalValue.Number(-5);
var arg = new NumberLocalValue(-5);

Assert.That(async () =>
{
Expand All @@ -163,7 +195,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentNumberZero()
{
var arg = new LocalValue.Number(0);
var arg = new NumberLocalValue(0);

Assert.That(async () =>
{
Expand All @@ -182,7 +214,7 @@ await context.Script.CallFunctionAsync($$"""
[IgnoreBrowser(Selenium.Browser.Chrome, "Chromium can't handle -0 argument as a number: https://github.com/w3c/webdriver-bidi/issues/887")]
public void CanCallFunctionWithArgumentNumberNegativeZero()
{
var arg = new LocalValue.Number(double.NegativeZero);
var arg = new NumberLocalValue(double.NegativeZero);

Assert.That(async () =>
{
Expand All @@ -199,7 +231,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentNumberPositiveInfinity()
{
var arg = new LocalValue.Number(double.PositiveInfinity);
var arg = new NumberLocalValue(double.PositiveInfinity);

Assert.That(async () =>
{
Expand All @@ -216,7 +248,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentNumberNegativeInfinity()
{
var arg = new LocalValue.Number(double.NegativeInfinity);
var arg = new NumberLocalValue(double.NegativeInfinity);

Assert.That(async () =>
{
Expand All @@ -233,7 +265,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentNumberNaN()
{
var arg = new LocalValue.Number(double.NaN);
var arg = new NumberLocalValue(double.NaN);
Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
Expand All @@ -249,7 +281,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentRegExp()
{
var arg = new LocalValue.RegExp(new LocalValue.RegExp.RegExpValue("foo*") { Flags = "g" });
var arg = new RegExpLocalValue(new RegExpLocalValue.RegExpValue("foo*") { Flags = "g" });

Assert.That(async () =>
{
Expand All @@ -266,7 +298,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentArray()
{
var arg = new LocalValue.Array([new LocalValue.String("hi")]);
var arg = new ArrayLocalValue([new StringLocalValue("hi")]);

Assert.That(async () =>
{
Expand All @@ -283,7 +315,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentObject()
{
var arg = new LocalValue.Object([[new LocalValue.String("objKey"), new LocalValue.String("objValue")]]);
var arg = new ObjectLocalValue([[new StringLocalValue("objKey"), new StringLocalValue("objValue")]]);

Assert.That(async () =>
{
Expand All @@ -300,7 +332,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentMap()
{
var arg = new LocalValue.Map([[new LocalValue.String("mapKey"), new LocalValue.String("mapValue")]]);
var arg = new MapLocalValue([[new StringLocalValue("mapKey"), new StringLocalValue("mapValue")]]);

Assert.That(async () =>
{
Expand All @@ -317,7 +349,7 @@ await context.Script.CallFunctionAsync($$"""
[Test]
public void CanCallFunctionWithArgumentSet()
{
var arg = new LocalValue.Set([new LocalValue.String("setKey")]);
var arg = new SetLocalValue([new StringLocalValue("setKey")]);

Assert.That(async () =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ async function() {
[Test]
public async Task CanCallFunctionWithThisParameter()
{
var thisParameter = new LocalValue.Object([["some_property", 42]]);
var thisParameter = new ObjectLocalValue([["some_property", 42]]);

var res = await context.Script.CallFunctionAsync<int>("""
function(){return this.some_property}
Expand Down
4 changes: 2 additions & 2 deletions dotnet/test/common/BiDi/Script/ScriptCommandsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public async Task CanAddPreloadScriptWithArguments()
{
var preloadScript = await bidi.Script.AddPreloadScriptAsync("(channel) => channel('will_be_send', 'will_be_ignored')", new()
{
Arguments = [new LocalValue.Channel(new(new("channel_name")))]
Arguments = [new ChannelLocalValue(new(new("channel_name")))]
});

Assert.That(preloadScript, Is.Not.Null);
Expand All @@ -122,7 +122,7 @@ public async Task CanAddPreloadScriptWithChannelOptions()
{
var preloadScript = await bidi.Script.AddPreloadScriptAsync("(channel) => channel('will_be_send', 'will_be_ignored')", new()
{
Arguments = [new LocalValue.Channel(new(new("channel_name"))
Arguments = [new ChannelLocalValue(new(new("channel_name"))
{
SerializationOptions = new()
{
Expand Down
Loading
Loading