Skip to content

Commit 5bd9f79

Browse files
committed
First take at refactoring DriverOptions for .NET
This commit deprecates the AddAdditionalCapability method in the driver-specific Options classes in favor of two methods. The first, AddAdditionalOption, adds a capability to the top-level, global section of a browser's desired capabilities section. The second method adds a capability to a browser's specific set of options. Accordingly, these methods are different for each browser's Options class (AddAdditionalChromeOption for ChromeOptions, AddAdditionalFirefoxOption for FirefoxOptions, AddAdditionalInternetExplorerOption for InternetExplorerOptions, etc.). Also, this commit completes the removal of the DesiredCapabilities class by removing its visibility from the public API. All use cases that previously required adding arbitrary capabilities to a DesiredCapabilities instance should now be manageable by the browser- specific options classes. Moreover, the ToCapabilities method of the options classes now returns a read-only ICapabilities object. Users who find these structures insufficient are encouraged to join the project IRC or Slack channels to discuss where the deficiencies lie. Likewise, downstream projects (like Appium) and cloud providers (like SauceLabs, BrowserStack, etc.) that depend on the .NET language bindings for functionality should be aware of this change, and should take immediate steps to update their user-facing code and documentation to match.
1 parent 9694110 commit 5bd9f79

13 files changed

+230
-271
lines changed

dotnet/src/webdriver/Chrome/ChromeOptions.cs

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ public class ChromeOptions : DriverOptions
8888
private List<string> encodedExtensions = new List<string>();
8989
private List<string> excludedSwitches = new List<string>();
9090
private List<string> windowTypes = new List<string>();
91-
private Dictionary<string, object> additionalCapabilities = new Dictionary<string, object>();
9291
private Dictionary<string, object> additionalChromeOptions = new Dictionary<string, object>();
9392
private Dictionary<string, object> userProfilePreferences;
9493
private Dictionary<string, object> localStatePreferences;
@@ -492,6 +491,27 @@ public void AddWindowTypes(IEnumerable<string> windowTypesToAdd)
492491
this.windowTypes.AddRange(windowTypesToAdd);
493492
}
494493

494+
/// <summary>
495+
/// Provides a means to add additional capabilities not yet added as type safe options
496+
/// for the Chrome driver.
497+
/// </summary>
498+
/// <param name="optionName">The name of the capability to add.</param>
499+
/// <param name="optionValue">The value of the capability to add.</param>
500+
/// <exception cref="ArgumentException">
501+
/// thrown when attempting to add a capability for which there is already a type safe option, or
502+
/// when <paramref name="optionName"/> is <see langword="null"/> or the empty string.
503+
/// </exception>
504+
/// <remarks>Calling <see cref="AddAdditionalChromeOption(string, object)"/>
505+
/// where <paramref name="optionName"/> has already been added will overwrite the
506+
/// existing value with the new value in <paramref name="optionValue"/>.
507+
/// Calling this method adds capabilities to the Chrome-specific options object passed to
508+
/// chromedriver.exe (property name 'goog:chromeOptions').</remarks>
509+
public void AddAdditionalChromeOption(string optionName, object optionValue)
510+
{
511+
this.ValidateCapabilityName(optionName);
512+
this.additionalChromeOptions[optionName] = optionValue;
513+
}
514+
495515
/// <summary>
496516
/// Provides a means to add additional capabilities not yet added as type safe options
497517
/// for the Chrome driver.
@@ -507,6 +527,7 @@ public void AddWindowTypes(IEnumerable<string> windowTypesToAdd)
507527
/// existing value with the new value in <paramref name="capabilityValue"/>.
508528
/// Also, by default, calling this method adds capabilities to the options object passed to
509529
/// chromedriver.exe.</remarks>
530+
[Obsolete("Use the temporary AddAdditionalOption method or the AddAdditionalChromeOption method for adding additional options")]
510531
public override void AddAdditionalCapability(string capabilityName, object capabilityValue)
511532
{
512533
// Add the capability to the chromeOptions object by default. This is to handle
@@ -530,35 +551,16 @@ public override void AddAdditionalCapability(string capabilityName, object capab
530551
/// <remarks>Calling <see cref="AddAdditionalCapability(string, object, bool)"/>
531552
/// where <paramref name="capabilityName"/> has already been added will overwrite the
532553
/// existing value with the new value in <paramref name="capabilityValue"/></remarks>
554+
[Obsolete("Use the temporary AddAdditionalOption method or the AddAdditionalChromeOption method for adding additional options")]
533555
public void AddAdditionalCapability(string capabilityName, object capabilityValue, bool isGlobalCapability)
534556
{
535-
if (this.IsKnownCapabilityName(capabilityName))
536-
{
537-
string typeSafeOptionName = this.GetTypeSafeOptionName(capabilityName);
538-
string message = string.Format(CultureInfo.InvariantCulture, "There is already an option for the {0} capability. Please use the {1} instead.", capabilityName, typeSafeOptionName);
539-
540-
// TODO: Remove this if block when chromedriver bug 2371 is fixed
541-
// (https://bugs.chromium.org/p/chromedriver/issues/detail?id=2371)
542-
if (capabilityName == ForceAlwaysMatchCapabilityName)
543-
{
544-
message = string.Format(CultureInfo.InvariantCulture, "The {0} capability is internal to the driver, and not intended to be set from users' code. Do not attempt to set this capability.", capabilityName);
545-
}
546-
547-
throw new ArgumentException(message, "capabilityName");
548-
}
549-
550-
if (string.IsNullOrEmpty(capabilityName))
551-
{
552-
throw new ArgumentException("Capability name may not be null an empty string.", "capabilityName");
553-
}
554-
555557
if (isGlobalCapability)
556558
{
557-
this.additionalCapabilities[capabilityName] = capabilityValue;
559+
this.AddAdditionalOption(capabilityName, capabilityValue);
558560
}
559561
else
560562
{
561-
this.additionalChromeOptions[capabilityName] = capabilityValue;
563+
this.AddAdditionalChromeOption(capabilityName, capabilityValue);
562564
}
563565
}
564566

@@ -572,7 +574,7 @@ public override ICapabilities ToCapabilities()
572574
{
573575
Dictionary<string, object> chromeOptions = this.BuildChromeOptionsDictionary();
574576

575-
DesiredCapabilities capabilities = this.GenerateDesiredCapabilities(false);
577+
IWritableCapabilities capabilities = this.GenerateDesiredCapabilities(false);
576578
capabilities.SetCapability(ChromeOptions.Capability, chromeOptions);
577579

578580
Dictionary<string, object> loggingPreferences = this.GenerateLoggingPreferencesDictionary();
@@ -581,11 +583,6 @@ public override ICapabilities ToCapabilities()
581583
capabilities.SetCapability(CapabilityType.LoggingPreferences, loggingPreferences);
582584
}
583585

584-
foreach (KeyValuePair<string, object> pair in this.additionalCapabilities)
585-
{
586-
capabilities.SetCapability(pair.Key, pair.Value);
587-
}
588-
589586
return capabilities.AsReadOnly();
590587
}
591588

dotnet/src/webdriver/DriverOptions.cs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using OpenQA.Selenium.Remote;
2222
using System;
2323
using System.Collections.Generic;
24+
using System.Globalization;
2425

2526
namespace OpenQA.Selenium
2627
{
@@ -180,6 +181,26 @@ public Proxy Proxy
180181
set { this.proxy = value; }
181182
}
182183

184+
/// <summary>
185+
/// Provides a means to add additional capabilities not yet added as type safe options
186+
/// for the specific browser driver.
187+
/// </summary>
188+
/// <param name="optionName">The name of the capability to add.</param>
189+
/// <param name="optionValue">The value of the capability to add.</param>
190+
/// <exception cref="ArgumentException">
191+
/// thrown when attempting to add a capability for which there is already a type safe option, or
192+
/// when <paramref name="optionName"/> is <see langword="null"/> or the empty string.
193+
/// </exception>
194+
/// <remarks>Calling <see cref="AddAdditionalOption(string, object)"/>
195+
/// where <paramref name="optionName"/> has already been added will overwrite the
196+
/// existing value with the new value in <paramref name="optionValue"/>.
197+
/// </remarks>
198+
public virtual void AddAdditionalOption(string optionName, object optionValue)
199+
{
200+
this.ValidateCapabilityName(optionName);
201+
this.additionalCapabilities[optionName] = optionValue;
202+
}
203+
183204
/// <summary>
184205
/// Provides a means to add additional capabilities not yet added as type safe options
185206
/// for the specific browser driver.
@@ -194,6 +215,7 @@ public Proxy Proxy
194215
/// where <paramref name="capabilityName"/> has already been added will overwrite the
195216
/// existing value with the new value in <paramref name="capabilityValue"/>.
196217
/// </remarks>
218+
[Obsolete("Use the temporary AddAdditionalOption method or the browser-specific method for adding additional options")]
197219
public abstract void AddAdditionalCapability(string capabilityName, object capabilityValue);
198220

199221
/// <summary>
@@ -288,6 +310,30 @@ internal Dictionary<string, object> ToDictionary()
288310
return desired.CapabilitiesDictionary;
289311
}
290312

313+
/// <summary>
314+
/// Validates the name of the capability to verify it is not a capability
315+
/// for which a type-safe property or method already exists.
316+
/// </summary>
317+
/// <param name="capabilityName">The name of the capability to validate.</param>
318+
/// <exception cref="ArgumentException">
319+
/// thrown when attempting to add a capability for which there is already a type safe option, or
320+
/// when <paramref name="capabilityName"/> is <see langword="null"/> or the empty string.
321+
/// </exception>
322+
protected void ValidateCapabilityName(string capabilityName)
323+
{
324+
if (string.IsNullOrEmpty(capabilityName))
325+
{
326+
throw new ArgumentException("Capability name may not be null an empty string.", "capabilityName");
327+
}
328+
329+
if (this.IsKnownCapabilityName(capabilityName))
330+
{
331+
string typeSafeOptionName = this.GetTypeSafeOptionName(capabilityName);
332+
string message = string.Format(CultureInfo.InvariantCulture, "There is already an option for the {0} capability. Please use the {1} instead.", capabilityName, typeSafeOptionName);
333+
throw new ArgumentException(message, "capabilityName");
334+
}
335+
}
336+
291337
/// <summary>
292338
/// Adds a known capability to the list of known capabilities and associates it
293339
/// with the type-safe property name of the options class to be used instead.
@@ -348,8 +394,8 @@ protected Dictionary<string, object> GenerateLoggingPreferencesDictionary()
348394
/// Generates the current options as a capabilities object for further processing.
349395
/// </summary>
350396
/// <param name="isSpecificationCompliant">A value indicating whether to generate capabilities compliant with the W3C WebDriver Specification.</param>
351-
/// <returns>A <see cref="DesiredCapabilities"/> object representing the current options for further processing.</returns>
352-
protected DesiredCapabilities GenerateDesiredCapabilities(bool isSpecificationCompliant)
397+
/// <returns>A <see cref="IWritableCapabilities"/> object representing the current options for further processing.</returns>
398+
protected IWritableCapabilities GenerateDesiredCapabilities(bool isSpecificationCompliant)
353399
{
354400
DesiredCapabilities capabilities = new DesiredCapabilities();
355401
if (!string.IsNullOrEmpty(this.browserName))
@@ -428,6 +474,11 @@ protected DesiredCapabilities GenerateDesiredCapabilities(bool isSpecificationCo
428474
}
429475
}
430476

477+
foreach (KeyValuePair<string, object> pair in this.additionalCapabilities)
478+
{
479+
capabilities.SetCapability(pair.Key, pair.Value);
480+
}
481+
431482
return capabilities;
432483
}
433484
}

dotnet/src/webdriver/Edge/EdgeOptions.cs

Lines changed: 7 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,6 @@
2323

2424
namespace OpenQA.Selenium.Edge
2525
{
26-
/// <summary>
27-
/// Specifies the behavior of waiting for page loads in the Edge driver.
28-
/// </summary>
29-
public enum EdgePageLoadStrategy
30-
{
31-
/// <summary>
32-
/// Indicates the behavior is not set.
33-
/// </summary>
34-
Default,
35-
36-
/// <summary>
37-
/// Waits for pages to load and ready state to be 'complete'.
38-
/// </summary>
39-
Normal,
40-
41-
/// <summary>
42-
/// Waits for pages to load and for ready state to be 'interactive' or 'complete'.
43-
/// </summary>
44-
Eager,
45-
46-
/// <summary>
47-
/// Does not wait for pages to load, returning immediately.
48-
/// </summary>
49-
None
50-
}
51-
5226
/// <summary>
5327
/// Class to manage options specific to <see cref="EdgeDriver"/>
5428
/// </summary>
@@ -76,15 +50,16 @@ public class EdgeOptions : DriverOptions
7650
private const string ExtensionPathsCapability = "ms:extensionPaths";
7751
private const string StartPageCapability = "ms:startPage";
7852

79-
private EdgePageLoadStrategy pageLoadStrategy = EdgePageLoadStrategy.Default;
80-
private Dictionary<string, object> additionalCapabilities = new Dictionary<string, object>();
8153
private bool useInPrivateBrowsing;
8254
private string startPage;
8355
private List<string> extensionPaths = new List<string>();
8456

8557
public EdgeOptions() : base()
8658
{
8759
this.BrowserName = BrowserNameValue;
60+
this.AddKnownCapabilityName(UseInPrivateBrowsingCapability, "UseInPrivateBrowsing property");
61+
this.AddKnownCapabilityName(StartPageCapability, "StartPage property");
62+
this.AddKnownCapabilityName(ExtensionPathsCapability, "AddExtensionPaths method");
8863
}
8964

9065
/// <summary>
@@ -156,14 +131,10 @@ public void AddExtensionPaths(IEnumerable<string> extensionPathsToAdd)
156131
/// </exception>
157132
/// <remarks>Calling <see cref="AddAdditionalCapability"/> where <paramref name="capabilityName"/>
158133
/// has already been added will overwrite the existing value with the new value in <paramref name="capabilityValue"/></remarks>
134+
[Obsolete("Use the temporary AddAdditionalOption method for adding additional options")]
159135
public override void AddAdditionalCapability(string capabilityName, object capabilityValue)
160136
{
161-
if (string.IsNullOrEmpty(capabilityName))
162-
{
163-
throw new ArgumentException("Capability name may not be null an empty string.", "capabilityName");
164-
}
165-
166-
this.additionalCapabilities[capabilityName] = capabilityValue;
137+
this.AddAdditionalOption(capabilityName, capabilityValue);
167138
}
168139

169140
/// <summary>
@@ -174,7 +145,7 @@ public override void AddAdditionalCapability(string capabilityName, object capab
174145
/// <returns>The DesiredCapabilities for Edge with these options.</returns>
175146
public override ICapabilities ToCapabilities()
176147
{
177-
DesiredCapabilities capabilities = this.GenerateDesiredCapabilities(false);
148+
IWritableCapabilities capabilities = this.GenerateDesiredCapabilities(true);
178149

179150
if (this.useInPrivateBrowsing)
180151
{
@@ -191,13 +162,7 @@ public override ICapabilities ToCapabilities()
191162
capabilities.SetCapability(ExtensionPathsCapability, this.extensionPaths);
192163
}
193164

194-
foreach (KeyValuePair<string, object> pair in this.additionalCapabilities)
195-
{
196-
capabilities.SetCapability(pair.Key, pair.Value);
197-
}
198-
199-
// Should return capabilities.AsReadOnly(), and will in a future release.
200-
return capabilities;
165+
return capabilities.AsReadOnly();
201166
}
202167
}
203168
}

0 commit comments

Comments
 (0)