Skip to content

Commit e260b5b

Browse files
committed
[dotnet] Allow RemoteSessionSettings to use System.Text.Json values
Fixes #14725
1 parent 3589d3f commit e260b5b

File tree

2 files changed

+99
-34
lines changed

2 files changed

+99
-34
lines changed

dotnet/src/webdriver/Remote/RemoteSessionSettings.cs

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
using System.Collections.Generic;
2323
using System.Globalization;
2424
using System.Text.Json;
25+
using System.Text.Json.Nodes;
2526

2627
namespace OpenQA.Selenium
2728
{
@@ -34,7 +35,6 @@ public class RemoteSessionSettings : ICapabilities
3435
private const string AlwaysMatchCapabilityName = "alwaysMatch";
3536

3637
private readonly List<string> reservedSettingNames = new List<string>() { FirstMatchCapabilityName, AlwaysMatchCapabilityName };
37-
private DriverOptions mustMatchDriverOptions;
3838
private List<DriverOptions> firstMatchOptions = new List<DriverOptions>();
3939
private Dictionary<string, object> remoteMetadataSettings = new Dictionary<string, object>();
4040

@@ -60,7 +60,7 @@ public RemoteSessionSettings()
6060
/// </param>
6161
public RemoteSessionSettings(DriverOptions mustMatchDriverOptions, params DriverOptions[] firstMatchDriverOptions)
6262
{
63-
this.mustMatchDriverOptions = mustMatchDriverOptions;
63+
this.MustMatchDriverOptions = mustMatchDriverOptions;
6464
foreach (DriverOptions firstMatchOption in firstMatchDriverOptions)
6565
{
6666
this.AddFirstMatchDriverOption(firstMatchOption);
@@ -70,18 +70,12 @@ public RemoteSessionSettings(DriverOptions mustMatchDriverOptions, params Driver
7070
/// <summary>
7171
/// Gets a value indicating the options that must be matched by the remote end to create a session.
7272
/// </summary>
73-
internal DriverOptions MustMatchDriverOptions
74-
{
75-
get { return this.mustMatchDriverOptions; }
76-
}
73+
internal DriverOptions MustMatchDriverOptions { get; private set; }
7774

7875
/// <summary>
7976
/// Gets a value indicating the number of options that may be matched by the remote end to create a session.
8077
/// </summary>
81-
internal int FirstMatchOptionsCount
82-
{
83-
get { return this.firstMatchOptions.Count; }
84-
}
78+
internal int FirstMatchOptionsCount => this.firstMatchOptions.Count;
8579

8680
/// <summary>
8781
/// Gets the capability value with the specified name.
@@ -91,6 +85,7 @@ internal int FirstMatchOptionsCount
9185
/// <exception cref="ArgumentException">
9286
/// The specified capability name is not in the set of capabilities.
9387
/// </exception>
88+
/// <exception cref="ArgumentNullException">If <paramref name="capabilityName"/> is null.</exception>
9489
public object this[string capabilityName]
9590
{
9691
get
@@ -145,7 +140,7 @@ public void AddMetadataSetting(string settingName, object settingValue)
145140
throw new ArgumentException(string.Format("'{0}' is a reserved name for a metadata setting, and cannot be used as a name.", settingName), nameof(settingName));
146141
}
147142

148-
if (!this.IsJsonSerializable(settingValue))
143+
if (!IsJsonSerializable(settingValue))
149144
{
150145
throw new ArgumentException("Metadata setting value must be JSON serializable.", nameof(settingValue));
151146
}
@@ -160,9 +155,9 @@ public void AddMetadataSetting(string settingName, object settingValue)
160155
/// <param name="options">The <see cref="DriverOptions"/> to add to the list of "first matched" options.</param>
161156
public void AddFirstMatchDriverOption(DriverOptions options)
162157
{
163-
if (mustMatchDriverOptions != null)
158+
if (MustMatchDriverOptions != null)
164159
{
165-
DriverOptionsMergeResult mergeResult = mustMatchDriverOptions.GetMergeResult(options);
160+
DriverOptionsMergeResult mergeResult = MustMatchDriverOptions.GetMergeResult(options);
166161
if (mergeResult.IsMergeConflict)
167162
{
168163
string msg = string.Format(CultureInfo.InvariantCulture, "You cannot request the same capability in both must-match and first-match capabilities. You are attempting to add a first-match driver options object that defines a capability, '{0}', that is already defined in the must-match driver options.", mergeResult.MergeConflictOptionName);
@@ -197,7 +192,7 @@ public void SetMustMatchDriverOptions(DriverOptions options)
197192
}
198193
}
199194

200-
this.mustMatchDriverOptions = options;
195+
this.MustMatchDriverOptions = options;
201196
}
202197

203198
/// <summary>
@@ -255,7 +250,7 @@ public Dictionary<string, object> ToDictionary()
255250
capabilitiesDictionary[remoteMetadataSetting.Key] = remoteMetadataSetting.Value;
256251
}
257252

258-
if (this.mustMatchDriverOptions != null)
253+
if (this.MustMatchDriverOptions != null)
259254
{
260255
capabilitiesDictionary["alwaysMatch"] = GetAlwaysMatchOptionsAsSerializableDictionary();
261256
}
@@ -291,12 +286,12 @@ internal DriverOptions GetFirstMatchDriverOptions(int firstMatchIndex)
291286

292287
private IDictionary<string, object> GetAlwaysMatchOptionsAsSerializableDictionary()
293288
{
294-
return this.mustMatchDriverOptions.ToDictionary();
289+
return this.MustMatchDriverOptions.ToDictionary();
295290
}
296291

297292
private List<object> GetFirstMatchOptionsAsSerializableList()
298293
{
299-
List<object> optionsMatches = new List<object>();
294+
List<object> optionsMatches = new List<object>(this.firstMatchOptions.Count);
300295
foreach (DriverOptions options in this.firstMatchOptions)
301296
{
302297
optionsMatches.Add(options.ToDictionary());
@@ -305,34 +300,42 @@ private List<object> GetFirstMatchOptionsAsSerializableList()
305300
return optionsMatches;
306301
}
307302

308-
private bool IsJsonSerializable(object arg)
303+
private static bool IsJsonSerializable(object arg)
309304
{
310-
IEnumerable argAsEnumerable = arg as IEnumerable;
311-
IDictionary argAsDictionary = arg as IDictionary;
305+
if (arg is null)
306+
{
307+
return true;
308+
}
312309

313-
if (arg is string || arg is float || arg is double || arg is int || arg is long || arg is bool || arg == null)
310+
if (arg is string or float or double or int or long or bool)
314311
{
315312
return true;
316313
}
317-
else if (argAsDictionary != null)
314+
315+
if (arg is JsonNode or JsonElement)
318316
{
319-
foreach (object key in argAsDictionary.Keys)
317+
return true;
318+
}
319+
320+
if (arg is IDictionary argAsDictionary)
321+
{
322+
foreach (DictionaryEntry item in argAsDictionary)
320323
{
321-
if (!(key is string))
324+
if (item.Key is not string)
322325
{
323326
return false;
324327
}
325-
}
326328

327-
foreach (object value in argAsDictionary.Values)
328-
{
329-
if (!IsJsonSerializable(value))
329+
if (!IsJsonSerializable(item.Value))
330330
{
331331
return false;
332332
}
333333
}
334+
335+
return true;
334336
}
335-
else if (argAsEnumerable != null)
337+
338+
if (arg is IEnumerable argAsEnumerable)
336339
{
337340
foreach (object item in argAsEnumerable)
338341
{
@@ -341,13 +344,11 @@ private bool IsJsonSerializable(object arg)
341344
return false;
342345
}
343346
}
344-
}
345-
else
346-
{
347-
return false;
347+
348+
return true;
348349
}
349350

350-
return true;
351+
return false;
351352
}
352353
}
353354
}

dotnet/test/remote/RemoteSessionCreationTests.cs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using NUnit.Framework;
2+
using System.Collections.Generic;
3+
using System.Text.Json;
4+
using System.Text.Json.Nodes;
25

36
namespace OpenQA.Selenium.Remote
47
{
@@ -49,5 +52,66 @@ public void CreateEdgeRemoteSession()
4952
edge.Quit();
5053
}
5154
}
55+
56+
[Test]
57+
public void ShouldSetRemoteSessionSettingsMetadata()
58+
{
59+
var settings = new RemoteSessionSettings();
60+
61+
Assert.That(settings.HasCapability("a"), Is.False);
62+
63+
settings.AddMetadataSetting("a", null);
64+
Assert.That(settings.HasCapability("a"));
65+
Assert.That(settings.GetCapability("a"), Is.Null);
66+
67+
settings.AddMetadataSetting("a", true);
68+
Assert.That(settings.HasCapability("a"));
69+
Assert.That(settings.GetCapability("a"), Is.True);
70+
71+
settings.AddMetadataSetting("a", false);
72+
Assert.That(settings.HasCapability("a"));
73+
Assert.That(settings.GetCapability("a"), Is.False);
74+
75+
settings.AddMetadataSetting("a", 123);
76+
Assert.That(settings.HasCapability("a"));
77+
Assert.That(settings.GetCapability("a"), Is.TypeOf<int>().And.EqualTo(123));
78+
79+
settings.AddMetadataSetting("a", 123f);
80+
Assert.That(settings.HasCapability("a"));
81+
Assert.That(settings.GetCapability("a"), Is.TypeOf<float>().And.EqualTo(123f));
82+
83+
settings.AddMetadataSetting("a", 123d);
84+
Assert.That(settings.HasCapability("a"));
85+
Assert.That(settings.GetCapability("a"), Is.TypeOf<double>().And.EqualTo(123d));
86+
87+
JsonNode trueName = JsonValue.Create(true);
88+
settings.AddMetadataSetting("a", trueName);
89+
Assert.That(settings.HasCapability("a"));
90+
Assert.That(settings.GetCapability("a"), Is.InstanceOf<JsonNode>().And.EqualTo(trueName).Using<JsonNode>(JsonNode.DeepEquals));
91+
92+
var reader = new Utf8JsonReader("false"u8);
93+
JsonElement trueElement = JsonElement.ParseValue(ref reader);
94+
95+
settings.AddMetadataSetting("a", trueElement);
96+
Assert.That(settings.HasCapability("a"));
97+
Assert.That(settings.GetCapability("a"), Is.TypeOf<JsonElement>().And.Matches<JsonElement>(static left =>
98+
{
99+
return left.ValueKind == JsonValueKind.False;
100+
}));
101+
102+
List<int> intValues = [1, 2, 3];
103+
settings.AddMetadataSetting("a", intValues);
104+
Assert.That(settings.HasCapability("a"));
105+
Assert.That(settings.GetCapability("a"), Is.TypeOf<List<int>>().And.EqualTo(intValues));
106+
107+
Dictionary<string, int> dictionaryValues = new Dictionary<string, int>
108+
{
109+
{"value1", 1 },
110+
{"value2", 1 },
111+
};
112+
settings.AddMetadataSetting("a", dictionaryValues);
113+
Assert.That(settings.HasCapability("a"));
114+
Assert.That(settings.GetCapability("a"), Is.TypeOf<Dictionary<string, int>>().And.EqualTo(dictionaryValues));
115+
}
52116
}
53117
}

0 commit comments

Comments
 (0)