Skip to content

Commit

Permalink
[dotnet] Refactor registration and execution of custom WebDriver comm…
Browse files Browse the repository at this point in the history
…ands
  • Loading branch information
jimevans committed Nov 3, 2021
1 parent dcd36f6 commit d9f2bb8
Show file tree
Hide file tree
Showing 14 changed files with 739 additions and 233 deletions.
52 changes: 44 additions & 8 deletions dotnet/src/webdriver/Chrome/ChromeDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
// </copyright>

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using OpenQA.Selenium.Remote;
using OpenQA.Selenium.Chromium;
using System.Collections.Generic;

namespace OpenQA.Selenium.Chrome
{
Expand Down Expand Up @@ -58,12 +59,21 @@ namespace OpenQA.Selenium.Chrome
/// </example>
public class ChromeDriver : ChromiumDriver
{
private static Dictionary<string, CommandInfo> chromeCustomCommands = new Dictionary<string, CommandInfo>()
{
{ ExecuteCdp, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cdp/execute") },
{ GetCastSinksCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_sinks") },
{ SelectCastSinkCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/set_sink_to_use") },
{ StartCastTabMirroringCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/start_tab_mirroring") },
{ GetCastIssueMessageCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_issue_message") },
{ StopCastingCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/stop_casting") }
};

/// <summary>
/// Initializes a new instance of the <see cref="ChromeDriver"/> class.
/// </summary>
public ChromeDriver()
: this(new ChromeOptions())
: this(new ChromeOptions())
{
}

Expand Down Expand Up @@ -138,13 +148,39 @@ public ChromeDriver(ChromeDriverService service, ChromeOptions options)
public ChromeDriver(ChromeDriverService service, ChromeOptions options, TimeSpan commandTimeout)
: base(service, options, commandTimeout)
{
this.AddCustomChromeCommand(ExecuteCdp, HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cdp/execute");
this.AddCustomChromeCommand(GetCastSinksCommand, HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_sinks");
this.AddCustomChromeCommand(SelectCastSinkCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/set_sink_to_use");
this.AddCustomChromeCommand(StartCastTabMirroringCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/start_tab_mirroring");
this.AddCustomChromeCommand(GetCastIssueMessageCommand, HttpCommandInfo.GetCommand, "/session/{sessionId}/goog/cast/get_issue_message");
this.AddCustomChromeCommand(StopCastingCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/goog/cast/stop_casting");
this.AddCustomChromeCommands();
}

/// <summary>
/// Gets a read-only dictionary of the custom WebDriver commands defined for ChromeDriver.
/// The keys of the dictionary are the names assigned to the command; the values are the
/// <see cref="CommandInfo"/> objects describing the command behavior.
/// </summary>
public static IReadOnlyDictionary<string, CommandInfo> CustomCommandDefinitions
{
get
{
Dictionary<string, CommandInfo> customCommands = new Dictionary<string, CommandInfo>();
foreach (KeyValuePair<string, CommandInfo> entry in ChromiumCustomCommands)
{
customCommands[entry.Key] = entry.Value;
}

foreach (KeyValuePair<string, CommandInfo> entry in chromeCustomCommands)
{
customCommands[entry.Key] = entry.Value;
}

return new ReadOnlyDictionary<string, CommandInfo>(customCommands);
}
}

private void AddCustomChromeCommands()
{
foreach (KeyValuePair<string, CommandInfo> entry in CustomCommandDefinitions)
{
this.RegisterInternalDriverCommand(entry.Key, entry.Value);
}
}
}
}
139 changes: 87 additions & 52 deletions dotnet/src/webdriver/Chromium/ChromiumDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.ObjectModel;
using OpenQA.Selenium.DevTools;
using OpenQA.Selenium.Remote;

Expand All @@ -31,70 +27,107 @@ namespace OpenQA.Selenium.Chromium
/// <summary>
/// Provides an abstract way to access Chromium-based browsers to run tests.
/// </summary>
public abstract class ChromiumDriver : WebDriver, ISupportsLogs, IDevTools
public class ChromiumDriver : WebDriver, ISupportsLogs, IDevTools
{
/// <summary>
/// Accept untrusted SSL Certificates
/// </summary>
public static readonly bool AcceptUntrustedCertificates = true;

protected const string ExecuteCdp = "executeCdpCommand";
protected const string GetCastSinksCommand = "getCastSinks";
protected const string SelectCastSinkCommand = "selectCastSink";
protected const string StartCastTabMirroringCommand = "startCastTabMirroring";
protected const string GetCastIssueMessageCommand = "getCastIssueMessage";
protected const string StopCastingCommand = "stopCasting";
private const string GetNetworkConditionsCommand = "getNetworkConditions";
private const string SetNetworkConditionsCommand = "setNetworkConditions";
private const string DeleteNetworkConditionsCommand = "deleteNetworkConditions";
private const string SendChromeCommand = "sendChromeCommand";
private const string SendChromeCommandWithResult = "sendChromeCommandWithResult";
private const string LaunchAppCommand = "launchAppCommand";
private const string SetPermissionCommand = "setPermission";
/// <summary>
/// Command for executing a Chrome DevTools Protocol command in a driver for a Chromium-based browser.
/// </summary>
public static readonly string ExecuteCdp = "executeCdpCommand";

private readonly string optionsCapabilityName;
private DevToolsSession devToolsSession;
/// <summary>
/// Command for getting cast sinks in a driver for a Chromium-based browser.
/// </summary>
public static readonly string GetCastSinksCommand = "getCastSinks";

/// <summary>
/// Initializes a new instance of the <see cref="ChromiumDriver"/> class using the specified
/// <see cref="ChromiumDriverService"/> and options.
/// Command for selecting a cast sink in a driver for a Chromium-based browser.
/// </summary>
/// <param name="service">The <see cref="ChromiumDriverService"/> to use.</param>
/// <param name="options">The <see cref="ChromiumOptions"/> used to initialize the driver.</param>
public ChromiumDriver(ChromiumDriverService service, ChromiumOptions options)
: this(service, options, RemoteWebDriver.DefaultCommandTimeout)
public static readonly string SelectCastSinkCommand = "selectCastSink";

/// <summary>
/// Command for starting cast tab mirroring in a driver for a Chromium-based browser.
/// </summary>
public static readonly string StartCastTabMirroringCommand = "startCastTabMirroring";

/// <summary>
/// Command for getting a cast issued message in a driver for a Chromium-based browser.
/// </summary>
public static readonly string GetCastIssueMessageCommand = "getCastIssueMessage";

/// <summary>
/// Command for stopping casting in a driver for a Chromium-based browser.
/// </summary>
public static readonly string StopCastingCommand = "stopCasting";

/// <summary>
/// Command for getting the simulated network conditions in a driver for a Chromium-based browser.
/// </summary>
public static readonly string GetNetworkConditionsCommand = "getNetworkConditions";

/// <summary>
/// Command for setting the simulated network conditions in a driver for a Chromium-based browser.
/// </summary>
public static readonly string SetNetworkConditionsCommand = "setNetworkConditions";

/// <summary>
/// Command for deleting the simulated network conditions in a driver for a Chromium-based browser.
/// </summary>
public static readonly string DeleteNetworkConditionsCommand = "deleteNetworkConditions";

/// <summary>
/// Command for executing a Chrome DevTools Protocol command in a driver for a Chromium-based browser.
/// </summary>
public static readonly string SendChromeCommand = "sendChromeCommand";

/// <summary>
/// Command for executing a Chrome DevTools Protocol command that returns a result in a driver for a Chromium-based browser.
/// </summary>
public static readonly string SendChromeCommandWithResult = "sendChromeCommandWithResult";

/// <summary>
/// Command for launching an app in a driver for a Chromium-based browser.
/// </summary>
public static readonly string LaunchAppCommand = "launchAppCommand";

/// <summary>
/// Command for setting permissions in a driver for a Chromium-based browser.
/// </summary>
public static readonly string SetPermissionCommand = "setPermission";

private readonly string optionsCapabilityName;
private DevToolsSession devToolsSession;

private static Dictionary<string, CommandInfo> chromiumCustomCommands = new Dictionary<string, CommandInfo>()
{
}
{ GetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/chromium/network_conditions") },
{ SetNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/network_conditions") },
{ DeleteNetworkConditionsCommand, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/chromium/network_conditions") },
{ SendChromeCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/send_command") },
{ SendChromeCommandWithResult, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/send_command_and_get_result") },
{ LaunchAppCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/launch_app") },
{ SetPermissionCommand, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/permissions") }
};

/// <summary>
/// Initializes a new instance of the <see cref="ChromiumDriver"/> class using the specified <see cref="ChromiumDriverService"/>.
/// </summary>
/// <param name="service">The <see cref="ChromiumDriverService"/> to use.</param>
/// <param name="options">The <see cref="ChromiumOptions"/> to be used with the ChromiumDriver.</param>
/// <param name="commandTimeout">The maximum amount of time to wait for each command.</param>
public ChromiumDriver(ChromiumDriverService service, ChromiumOptions options, TimeSpan commandTimeout)
protected ChromiumDriver(ChromiumDriverService service, ChromiumOptions options, TimeSpan commandTimeout)
: base(new DriverServiceCommandExecutor(service, commandTimeout), ConvertOptionsToCapabilities(options))
{
this.optionsCapabilityName = options.CapabilityName;

// Add the custom commands unique to Chrome
this.AddCustomChromeCommand(GetNetworkConditionsCommand, HttpCommandInfo.GetCommand, "/session/{sessionId}/chromium/network_conditions");
this.AddCustomChromeCommand(SetNetworkConditionsCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/network_conditions");
this.AddCustomChromeCommand(DeleteNetworkConditionsCommand, HttpCommandInfo.DeleteCommand, "/session/{sessionId}/chromium/network_conditions");
this.AddCustomChromeCommand(SendChromeCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/send_command");
this.AddCustomChromeCommand(SendChromeCommandWithResult, HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/send_command_and_get_result");
this.AddCustomChromeCommand(LaunchAppCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/chromium/launch_app");
this.AddCustomChromeCommand(SetPermissionCommand, HttpCommandInfo.PostCommand, "/session/{sessionId}/permissions");
}

/// <summary>
/// Initializes a new instance of the <see cref="ChromiumDriver"/> class
/// </summary>
/// <param name="commandExecutor">An <see cref="ICommandExecutor"/> object which executes commands for the driver.</param>
/// <param name="desiredCapabilities">An <see cref="ICapabilities"/> object containing the desired capabilities of the browser.</param>
protected ChromiumDriver(ICommandExecutor commandExecutor, ICapabilities desiredCapabilities)
: base(commandExecutor, desiredCapabilities)
protected static IReadOnlyDictionary<string, CommandInfo> ChromiumCustomCommands
{
get { return new ReadOnlyDictionary<string, CommandInfo>(chromiumCustomCommands); }
}

/// <summary>
Expand Down Expand Up @@ -140,7 +173,7 @@ public ChromiumNetworkConditions NetworkConditions
}

Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters["network_conditions"] = value.ToDictionary();
parameters["network_conditions"] = value;
this.Execute(SetNetworkConditionsCommand, parameters);
}
}
Expand Down Expand Up @@ -291,6 +324,14 @@ public void CloseDevToolsSession()
}
}

/// <summary>
/// Clears simulated network conditions.
/// </summary>
public void ClearNetworkConditions()
{
this.Execute(DeleteNetworkConditionsCommand, null);
}

/// <summary>
/// Returns the list of cast sinks (Cast devices) available to the Chrome media router.
/// </summary>
Expand Down Expand Up @@ -401,11 +442,5 @@ private static ICapabilities ConvertOptionsToCapabilities(ChromiumOptions option

return options.ToCapabilities();
}

protected void AddCustomChromeCommand(string commandName, string method, string resourcePath)
{
HttpCommandInfo commandInfoToAdd = new HttpCommandInfo(method, resourcePath);
this.CommandExecutor.TryAddCommand(commandName, commandInfoToAdd);
}
}
}
67 changes: 44 additions & 23 deletions dotnet/src/webdriver/Chromium/ChromiumNetworkConditions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,25 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;

namespace OpenQA.Selenium.Chromium
{
/// <summary>
/// Provides manipulation of getting and setting network conditions from Chromium.
/// </summary>
[JsonObject(MemberSerialization.OptIn)]
public class ChromiumNetworkConditions
{
private bool offline;
private TimeSpan latency = TimeSpan.Zero;
private long downloadThroughput = -1;
private long uploadThroughput = -1;
private long downloadThroughput = 0;
private long uploadThroughput = 0;

/// <summary>
/// Gets or sets a value indicating whether the network is offline. Defaults to <see langword="false"/>.
/// </summary>
[JsonProperty("offline")]
public bool IsOffline
{
get { return this.offline; }
Expand All @@ -54,22 +55,49 @@ public TimeSpan Latency
/// <summary>
/// Gets or sets the throughput of the network connection in bytes/second for downloading.
/// </summary>
[JsonProperty("download_throughput")]
public long DownloadThroughput
{
get { return this.downloadThroughput; }
set { this.downloadThroughput = value; }
set
{
if (value < 0)
{
throw new WebDriverException("Downlod throughput cannot be negative.");
}

this.downloadThroughput = value;
}
}

/// <summary>
/// Gets or sets the throughput of the network connection in bytes/second for uploading.
/// </summary>
[JsonProperty("upload_throughput")]
public long UploadThroughput
{
get { return this.uploadThroughput; }
set { this.uploadThroughput = value; }
set
{
if (value < 0)
{
throw new WebDriverException("Upload throughput cannot be negative.");
}

this.uploadThroughput = value;
}
}

static internal ChromiumNetworkConditions FromDictionary(Dictionary<string, object> dictionary)
[JsonProperty("latency", NullValueHandling = NullValueHandling.Ignore)]
internal long? SerializableLatency
{
get
{
return Convert.ToInt64(this.latency.TotalMilliseconds);
}
}

public static ChromiumNetworkConditions FromDictionary(Dictionary<string, object> dictionary)
{
ChromiumNetworkConditions conditions = new ChromiumNetworkConditions();
if (dictionary.ContainsKey("offline"))
Expand All @@ -95,26 +123,19 @@ static internal ChromiumNetworkConditions FromDictionary(Dictionary<string, obje
return conditions;
}

internal Dictionary<string, object> ToDictionary()
/// <summary>
/// Sets the upload and download throughput properties to the same value.
/// </summary>
/// <param name="throughput">The throughput of the network connection in bytes/second for both upload and download.</param>
public void SetBidirectionalThroughput(long throughput)
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
dictionary["offline"] = this.offline;
if (this.latency != TimeSpan.Zero)
{
dictionary["latency"] = Convert.ToInt64(this.latency.TotalMilliseconds);
}

if (this.downloadThroughput >= 0)
{
dictionary["download_throughput"] = this.downloadThroughput;
}

if (this.uploadThroughput >= 0)
if (throughput < 0)
{
dictionary["upload_throughput"] = this.uploadThroughput;
throw new ArgumentException("Throughput values cannot be negative.", nameof(throughput));
}

return dictionary;
this.uploadThroughput = throughput;
this.downloadThroughput = throughput;
}
}
}
Loading

0 comments on commit d9f2bb8

Please sign in to comment.