Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ae2d622
[dotnet] Align `Scipt.LocalValue.Map` with spec
RenderMichael Mar 9, 2025
bd5aedc
Add test for Set
RenderMichael Mar 9, 2025
50f28b5
Remove obsolete assertion
RenderMichael Mar 9, 2025
9db00ab
Update new test name
RenderMichael Mar 9, 2025
d36e44e
Fix build
RenderMichael Mar 9, 2025
7e279bc
Replace TestCaseSource with individual tests
RenderMichael Mar 9, 2025
a91685b
Allow reading numbers from strings
RenderMichael Mar 9, 2025
83587d8
Separate CallFunction argument and return tests
RenderMichael Mar 10, 2025
9612f5f
Merge branch 'trunk' into bidi-map
RenderMichael Mar 10, 2025
e7a751d
Split tests into more classes
RenderMichael Mar 10, 2025
f51248e
remove unused imports
RenderMichael Mar 10, 2025
5c0f772
Properly serialize negative zero
RenderMichael Mar 10, 2025
d7c58f7
Fix variable name
RenderMichael Mar 10, 2025
45ecae9
fix formatting
RenderMichael Mar 10, 2025
0d8f906
Apply double converter directly to `RemoteValue.Number`
RenderMichael Mar 11, 2025
53310f7
Rename and clean up `DoubleConverter`
RenderMichael Mar 11, 2025
98cafd1
Clarify "is negative zero" check
RenderMichael Mar 11, 2025
6627a82
Merge branch 'trunk' into bidi-map
RenderMichael Mar 11, 2025
6c9c303
fix formatting
RenderMichael Mar 11, 2025
50abe67
Remove `BiDiDoubleConverter`
RenderMichael Mar 12, 2025
79b9274
Ignore failing tests
RenderMichael Mar 12, 2025
0a8d550
fix which tet is disabled
RenderMichael Mar 12, 2025
f2a615e
PR feedback
RenderMichael Mar 12, 2025
8c36bb2
PR feedback
RenderMichael Mar 12, 2025
034b533
Merge branch 'trunk' into bidi-map
RenderMichael Mar 12, 2025
1a7c7f3
PR test feedback
RenderMichael Mar 12, 2025
f4d3785
Rename local
RenderMichael Mar 12, 2025
d46e10f
fix further
RenderMichael Mar 12, 2025
d5c4954
Avoid magic of records for remote primitives
RenderMichael Mar 13, 2025
c51dc4c
Merge branch 'trunk' into bidi-map
RenderMichael Mar 13, 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
4 changes: 4 additions & 0 deletions dotnet/src/webdriver/BiDi/Communication/Broker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ internal Broker(BiDi bidi, ITransport transport)
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,

// BiDi returns special numbers such as "NaN" as strings
// Additionally, -0 is returned as a string "-0"
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals | JsonNumberHandling.AllowReadingFromString,
Converters =
{
new BrowsingContextConverter(_bidi),
Expand Down
7 changes: 2 additions & 5 deletions dotnet/src/webdriver/BiDi/Modules/Script/LocalValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,7 @@ public static LocalValue ConvertFrom(object? value)
}
}

public abstract record PrimitiveProtocolLocalValue : LocalValue
{

}
public abstract record PrimitiveProtocolLocalValue : LocalValue;

public record Number(double Value) : PrimitiveProtocolLocalValue
{
Expand Down Expand Up @@ -103,7 +100,7 @@ public record Array(IEnumerable<LocalValue> Value) : LocalValue;

public record Date(string Value) : LocalValue;

public record Map(IDictionary<string, LocalValue> Value) : LocalValue; // seems to implement IDictionary
public record Map(IEnumerable<IEnumerable<LocalValue>> Value) : LocalValue;

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

Expand Down
6 changes: 3 additions & 3 deletions dotnet/src/webdriver/BiDi/Modules/Script/RemoteValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ public abstract record RemoteValue
{
public static implicit operator int(RemoteValue remoteValue) => (int)((Number)remoteValue).Value;
public static implicit operator long(RemoteValue remoteValue) => (long)((Number)remoteValue).Value;
public static implicit operator string(RemoteValue remoteValue)
public static implicit operator string?(RemoteValue remoteValue)
{
return remoteValue switch
{
String stringValue => stringValue.Value,
Null => null!,
Null => null,
_ => throw new BiDiException($"Cannot convert {remoteValue} to string")
};
}
Expand Down Expand Up @@ -158,7 +158,7 @@ public record Map : RemoteValue

public InternalId? InternalId { get; set; }

public IDictionary<string, RemoteValue>? Value { get; set; }
public IReadOnlyList<IReadOnlyList<RemoteValue>>? Value { get; set; }
}

public record Set : RemoteValue
Expand Down
333 changes: 333 additions & 0 deletions dotnet/test/common/BiDi/Script/CallFunctionLocalValueTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
// <copyright file="CallFunctionLocalValueTest.cs" company="Selenium Committers">
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
// </copyright>

using NUnit.Framework;
using OpenQA.Selenium.BiDi.Modules.Script;

namespace OpenQA.Selenium.BiDi.Script;

class CallFunctionLocalValueTest : BiDiTestFixture
{
[Test]
public void CanCallFunctionWithArgumentUndefined()
{
var undefined = new LocalValue.Undefined();
Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (typeof arg !== 'undefined') {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [undefined] });
}, Throws.Nothing);
}

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

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

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

[Test]
public void CanCallFunctionWithArgumentRecentDate()
{
const string PinnedDateTimeString = "2025-03-09T00:30:33.083Z";

var date = new LocalValue.Date(PinnedDateTimeString);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg.toISOString() !== '{{PinnedDateTimeString}}') {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [date] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentEpochDate()
{
const string EpochString = "1970-01-01T00:00:00.000Z";

var epochDate = new LocalValue.Date(EpochString);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg.toISOString() !== '{{EpochString}}') {
throw new Error("Assert failed: " + arg.toISOString());
}
}
""", false, new() { Arguments = [epochDate] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentNumber5()
{
var number5 = new LocalValue.Number(5);

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

[Test]
public void CanCallFunctionWithArgumentNumberNegative5()
{
var numberMinus5 = new LocalValue.Number(-5);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg !== -5) {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [numberMinus5] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentNumber0()
{
var number0 = new LocalValue.Number(0);

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

[Test]
[IgnoreBrowser(Selenium.Browser.Edge, "Chromium can't handle -0 argument as a number: https://github.com/w3c/webdriver-bidi/issues/887")]
[IgnoreBrowser(Selenium.Browser.Chrome, "Chromium can't handle -0 argument as a number: https://github.com/w3c/webdriver-bidi/issues/887")]
public void CanCallFunctionWithArgumentNumberNegative0()
{
var minus0 = new LocalValue.Number(double.NegativeZero);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg !== 0 || arg.toLocaleString()[0] !== '-') {
throw new Error("Assert failed: " + arg.toLocaleString());
}
}
""", false, new() { Arguments = [minus0] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentNumberPositiveInfinity()
{
var infinity = new LocalValue.Number(double.PositiveInfinity);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg !== Number.POSITIVE_INFINITY) {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [infinity] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentNumberNegativeInfinity()
{
var minusInfinity = new LocalValue.Number(double.NegativeInfinity);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg !== Number.NEGATIVE_INFINITY) {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [minusInfinity] });
}, Throws.Nothing);
}

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

[Test]
public void CanCallFunctionWithArgumentRegExp()
{
var regex = new LocalValue.RegExp(new LocalValue.RegExp.RegExpValue("foo*") { Flags = "g" });

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (!arg.test('foo') || arg.source !== 'foo*' || !arg.global) {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [regex] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentArray()
{
var hiString = new LocalValue.Array([new LocalValue.String("hi")]);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg.length !== 1 || arg[0] !== 'hi') {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [hiString] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentObject()
{
var obj = new LocalValue.Object([[new LocalValue.String("objKey"), new LocalValue.String("objValue")]]);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg.objKey !== 'objValue' || Object.keys(arg).length !== 1) {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [obj] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentMap()
{
var arg = new LocalValue.Map([[new LocalValue.String("mapKey"), new LocalValue.String("mapValue")]]);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (arg.get('mapKey') !== 'mapValue' || arg.size !== 1) {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [arg] });
}, Throws.Nothing);
}

[Test]
public void CanCallFunctionWithArgumentSet()
{
var argument = new LocalValue.Set([new LocalValue.String("setKey")]);

Assert.That(async () =>
{
await context.Script.CallFunctionAsync($$"""
(arg) => {
if (!arg.has('setKey') || arg.size !== 1) {
throw new Error("Assert failed: " + arg);
}
}
""", false, new() { Arguments = [argument] });
}, Throws.Nothing);
}
}
Loading
Loading