using Selenium.Core;
using Selenium.Internal;
using Selenium.Serializer;
using System;
using System.Collections;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
namespace Selenium {
///
/// Object through which the user controls the browser.
///
///
/// VBScript:
///
/// Class Script
/// Dim driver
///
/// Sub Class_Initialize
/// Set driver = CreateObject("Selenium.WebDriver")
/// driver.Start "firefox", "http://www.google.com"
/// driver.Get "/"
/// End Sub
///
/// Sub Class_Terminate
/// driver.Quit
/// End Sub
/// End Class
///
/// Set s = New Script
///
///
/// VBA:
///
/// Public Sub Script()
/// Dim driver As New WebDriver
/// driver.Start "firefox", "http://www.google.com"
/// driver.Get "/"
/// ...
/// driver.Quit
/// End Sub
///
///
[ProgId("Selenium.WebDriver")]
[Guid("0277FC34-FD1B-4616-BB19-E3CCFFAB4234")]
[ComVisible(true), ClassInterface(ClassInterfaceType.None)]
[Description("Defines the interface through which the user controls the browser using WebDriver")]
public class WebDriver : SearchContext, ComInterfaces._WebDriver, IDisposable {
const string RUNNING_OBJECT_NAME = "Selenium.WebDriver";
internal Capabilities Capabilities = new Capabilities();
internal Dictionary Preferences = new Dictionary();
internal List Extensions = new List();
internal List Arguments = new List();
internal string Profile = null;
internal string Binary = null;
internal bool Persistant = false;
private Timeouts timeouts = new Timeouts();
private IDriverService _service = null;
private RemoteSession _session = null;
private string _baseUrl = null;
private Proxy _proxy = null;
private COMRunningObject _comRunningObj = null;
///
/// Creates a new WebDriver object.
///
public WebDriver() {
UnhandledException.Initialize();
RegisterRunningObject();
COMDisposable.Subscribe(this, typeof(ComInterfaces._WebDriver));
}
public WebDriver(string browser)
: this() {
this.Capabilities.BrowserName = browser;
}
~WebDriver() {
this.Dispose();
}
///
/// Release the resources.
///
public void Dispose() {
if (_comRunningObj != null) {
_comRunningObj.Dispose();
_comRunningObj = null;
}
if (_service != null) {
_service.Dispose();
_service = null;
}
if (_session != null) {
_session = null;
}
}
private void RegisterRunningObject(){
if (_comRunningObj == null)
_comRunningObj = new COMRunningObject(this, RUNNING_OBJECT_NAME);
}
#region Setup
///
///
///
public Proxy Proxy {
get {
return _proxy ?? (_proxy = new Proxy(Capabilities));
}
}
///
/// Set a specific profile for the firefox webdriver
///
/// Profil name (Firefox only) or directory (Firefox and Chrome)
/// If true, the browser will be launched without a copy the profile (Firefox only)
/// The profile directory can be copied from the user temp folder (run %temp%) before the WebDriver is stopped. It's also possible to create a new Firefox profile by launching firefox with the "-p" switch (firefox.exe -p).
///
///
/// Dim driver As New Selenium.FirefoxDriver
/// driver.SetProfile "Selenium" 'Firefox only. Profile created by running "..\firefox.exe -p"
/// driver.Get "http://www.google.com"
/// ...
///
///
/// Dim driver As New Selenium.FirefoxDriver
/// driver.SetProfile "C:\MyProfil" 'For Chrome and Firefox only
/// driver.Get "http://www.google.com"
/// ...
///
///
public void SetProfile(string nameOrDirectory, bool persistant = false) {
Profile = nameOrDirectory;
Persistant = persistant;
}
///
/// Set a specific preference for the firefox webdriver
///
/// Preference key
/// Preference value
public void SetPreference(string key, object value) {
Preferences[key] = JSON.Parse(value);
}
///
/// Set a specific capability for the webdriver
///
/// Capability key
/// Capability value
public void SetCapability(string key, object value) {
Capabilities[key] = JSON.Parse(value);
}
///
/// Add an extension to the browser (For Firefox and Chrome only)
///
/// Path to the extension
public void AddExtension(string extensionPath) {
Extensions.Add(new DriverExtension(extensionPath));
}
///
/// Add an argument to be appended to the command line to launch the browser.
///
/// Argument
public void AddArgument(string argument) {
Arguments.Add(argument);
}
///
/// Set the path to the browser executable to use
///
/// Full path
public void SetBinary(string path) {
if (!File.Exists(path))
throw new Errors.FileNotFoundError(path);
this.Binary = path;
}
#endregion
#region Session
internal override RemoteSession session {
get {
if (_session == null)
throw new Errors.BrowserNotStartedError();
return _session;
}
}
internal override string uri {
get {
return string.Empty;
}
}
///
/// Starts a new Selenium testing session
///
/// Name of the browser : firefox, ie, chrome, phantomjs
/// The base URL
///
///
/// Dim driver As New WebDriver()
/// driver.Start "firefox", "http://www.google.com"
/// driver.Get "/"
///
///
public void Start(string browser = null, string baseUrl = null) {
try {
browser = ExpendBrowserName(browser);
switch (browser) {
case "firefox":
_service = FirefoxDriver.StartService(this);
break;
case "chrome":
_service = ChromeDriver.StartService(this);
break;
case "phantomjs":
_service = PhantomJSDriver.StartService(this);
break;
case "internet explorer":
_service = IEDriver.StartService(this);
break;
case "MicrosoftEdge":
_service = EdgeDriver.StartService(this);
break;
case "opera":
_service = OperaDriver.StartService(this);
break;
default:
throw new Errors.ArgumentError("Invalid browser name: {0}", browser);
}
this.Capabilities.BrowserName = browser;
RegisterRunningObject();
_session = new RemoteSession(_service.Uri, true, this.timeouts);
_session.Start(this.Capabilities);
if (!string.IsNullOrEmpty(baseUrl))
this.BaseUrl = baseUrl;
} catch (SeleniumException) {
throw;
} catch (Exception ex) {
throw new SeleniumException(ex);
}
}
///
/// Starts remotely a new Selenium testing session
///
/// Remote executor address (ex : "http://localhost:4444/wd/hub")
/// Name of the browser : firefox, ie, chrome, phantomjs, htmlunit, htmlunitwithjavascript, android, ipad, opera
/// Browser version
/// Platform: WINDOWS, LINUX, MAC, ANDROID...
///
///
/// Dim driver As New WebDriver()
/// driver.StartRemotely "http://localhost:4444/wd/hub", "ie", 11
/// driver.Get "/"
///
///
public void StartRemotely(string executorUri, string browser = null, string version = null, string platform = null) {
try {
browser = ExpendBrowserName(browser);
switch (browser) {
case "firefox":
FirefoxDriver.ExtendCapabilities(this, true);
break;
case "chrome":
ChromeDriver.ExtendCapabilities(this, true);
break;
case "phantomjs":
PhantomJSDriver.ExtendCapabilities(this, true);
break;
case "internet explorer":
IEDriver.ExtendCapabilities(this, true);
break;
case "MicrosoftEdge":
EdgeDriver.ExtendCapabilities(this, true);
break;
case "opera":
OperaDriver.ExtendCapabilities(this, true);
break;
}
this.Capabilities.Platform = platform;
this.Capabilities.BrowserName = browser;
if (!string.IsNullOrEmpty(version))
this.Capabilities.BrowserVersion = version;
_session = new RemoteSession(executorUri, false, this.timeouts);
_session.Start(this.Capabilities);
RegisterRunningObject();
} catch (SeleniumException) {
throw;
} catch (Exception ex) {
throw new SeleniumException(ex);
}
}
///
/// Close the Browser and Dispose of WebDriver
///
public virtual void Quit() {
if (_session != null) {
try {
if (_session.IsLocal) {
if (!(_service is FirefoxService))
_session.Delete();
_service.Quit(_session.server);
} else {
_session.Delete();
}
} catch { }
}
this.Dispose();
}
private string ExpendBrowserName(string browser) {
if (string.IsNullOrEmpty(browser)) {
browser = this.Capabilities.BrowserName;
if (string.IsNullOrEmpty(browser))
throw new Errors.ArgumentError("Browser not defined");
}
string browserName = browser.ToLower();
switch (browserName) {
case "firefox":
case "ff":
return "firefox";
case "chrome":
case "cr":
return "chrome";
case "phantomjs":
case "pjs":
return "phantomjs";
case "internet explorer":
case "internetexplorer":
case "iexplore":
case "ie":
return "internet explorer";
case "microsoft edge":
case "microsoftedge":
case "edge":
return "MicrosoftEdge";
case "opera":
case "op":
return "opera";
default:
return browserName;
}
}
///
/// Sends a customized command
///
/// POST, GET or DELETE
/// Relative URI. Ex: "/screenshot"
/// Optional
///
/// Optional
///
/// Optional
///
/// Optional
///
/// Result
///
///
/// Set links = driver.Send("POST", "/elements", "using", "css selector", "value", "a")
///
///
public object Send(string method, string relativeUri,
string param1 = null, object value1 = null,
string param2 = null, object value2 = null,
string param3 = null, object value3 = null,
string param4 = null, object value4 = null) {
RequestMethod mth = 0;
switch (method.ToUpper()) {
case "POST": mth = RequestMethod.POST; break;
case "GET": mth = RequestMethod.GET; break;
case "DELETE": mth = RequestMethod.DELETE; break;
default:
throw new Errors.ArgumentError("Unhandled method: " + method);
}
Dictionary data = null;
if (param1 != null) {
data = new Dictionary();
data.Add(param1, value1);
if (param2 != null) {
data.Add(param2, value2);
if (param3 != null) {
data.Add(param3, value3);
if (param4 != null) {
data.Add(param4, value3);
}
}
}
}
object result = session.Send(mth, relativeUri, data);
return result;
}
#endregion
#region Interfaces
///
/// Manage the browser settings. Need to be defined before the browser is launched
///
public Timeouts Timeouts {
get {
return this.timeouts;
}
}
///
/// Instructs the driver to change its settings.
///
public Manage Manage {
get {
return this.session.manage;
}
}
///
/// Get the actions class
///
///
///
/// WebDriver driver = New WebDriver()
/// driver.start "firefox", "http://www.google.com"
/// driver.get "/"
/// driver.Actions.keyDown(Keys.Control).sendKeys("a").perform
///
///
public Actions Actions {
get {
return new Actions(this.session);
}
}
///
/// TouchActions
///
public TouchActions TouchActions {
get {
RemoteSession session = this.session;
return new TouchActions(session, this.TouchScreen);
}
}
///
/// Keyboard
///
public Keyboard Keyboard {
get {
return this.session.keyboard;
}
}
///
/// Mouse
///
public Mouse Mouse {
get {
return this.session.mouse;
}
}
///
/// TouchScreen
///
public TouchScreen TouchScreen {
get {
return this.session.touchscreen;
}
}
///
/// Keys
///
public Keys Keys {
get {
return this.session.keyboard.Keys;
}
}
#endregion
#region Navigation
///
/// Base URL to use a relative URL with Get
///
public string BaseUrl {
get {
return _baseUrl;
}
set {
_baseUrl = value.TrimEnd('/');
}
}
///
/// Loads a web page in the current browser session. Same as Open method.
///
/// URL
/// Optional timeout in milliseconds. Infinite=-1
/// Optional - Raise an exception after the timeout when true
/// Return true if the url was openned within the timeout, false otherwise
public bool Get(string url, int timeout = -1, bool raise = true) {
if (_session == null)
this.Start();
RemoteSession session = _session;
if (string.IsNullOrEmpty(url))
throw new Errors.ArgumentError("Argument 'url' cannot be null.");
if (timeout > 0){
session.timeouts.PageLoad = timeout;
session.Send(RequestMethod.POST, "/timeouts", "type", "page load", "ms", timeout);
}
int idx = url.IndexOf("/");
if (idx == 0) {
//relative url
if (_baseUrl == null)
throw new Errors.ArgumentError("Base URL not defined. Define a base URL or use a full URL.");
url = string.Concat(_baseUrl, url);
} else {
//absolute url
idx = url.IndexOf('/', idx + 3);
if (idx != -1) {
_baseUrl = url.Substring(0, idx - 1);
} else {
_baseUrl = url;
}
}
try {
session.Send(RequestMethod.POST, "/url", "url", url);
return true;
} catch {
if (raise)
throw;
return false;
}
}
///
/// Get the URL the browser is currently displaying.
///
/// Current URL
public string Url {
get {
return (string)session.Send(RequestMethod.GET, "/url");
}
}
///
/// Gets the title of the current browser window.
///
/// Title of the window
public string Title {
get {
return session.windows.CurrentWindow.Title;
}
}
///
/// Goes one step backward in the browser history.
///
public void GoBack() {
this.session.Send(RequestMethod.POST, "/back");
}
///
/// Goes one step forward in the browser history.
///
public void GoForward() {
this.session.Send(RequestMethod.POST, "/forward");
}
///
/// Refreshes the current page.
///
public void Refresh() {
this.session.Send(RequestMethod.POST, "/refresh");
}
#endregion
#region Windows
///
/// Gets an object allowing the user to manipulate the currently-focused browser window.
///
public Window Window {
get {
return session.windows.CurrentWindow;
}
}
///
/// Gets the window handles of open browser windows.
///
///
public List Windows {
get {
List windows, handles;
session.windows.ListWindows(out windows, out handles);
return windows;
}
}
///
/// Closes the current window.
///
public void Close() {
this.Window.Close();
}
#endregion
#region Interaction
///
/// Sends a sequence of keystrokes to the browser.
///
/// Sequence of keys or a modifier key(Control, Shift or Alt) if the sequence is in keysToSendEx
/// Optional - Sequence of keys if keysToSend contains modifier key(Control,Shift...)
///
/// To Send mobile to the window :
///
/// driver.SendKeys "mobile"
///
/// To Send ctrl+a to the window :
///
/// driver.SendKeys Keys.Control, "a"
///
///
public void SendKeys(string keysOrModifier, string keys = null) {
this.session.keyboard.SendKeys(keysOrModifier, keys);
}
#endregion
#region Screenshot
///
/// Takes the screenshot of the current window
///
/// Time to wait before taking the screenshot in milliseconds
///
public Image TakeScreenshot(int delay = 0) {
if (delay != 0)
SysWaiter.Wait(delay);
Image image = (Image)session.Send(RequestMethod.GET, "/screenshot");
return image;
}
#endregion
#region Javascript
///
/// Execute a piece of JavaScript in the context of the currently selected frame or window
///
/// The JavaScript code to execute.
/// The arguments to the script.
/// The value specified by the return statement.
///
///
/// txt = driver.ExecuteScript("return 'xyz' + arguments[0];", "123")
///
///
public object ExecuteScript(string script, object arguments = null) {
object args_ex = FormatArguments(arguments);
object result = session.javascript.Execute(script, args_ex, true);
return result;
}
///
/// Execute an asynchronous piece of JavaScript in the context of the current frame or window.
///
/// Piece of JavaScript code to execute.
/// Optional arguments for the script.
/// Optional timeout in milliseconds.
/// The first argument of the callback function.
///
///
/// txt = driver.ExecuteAsyncScript("callback('xyz')");"
///
///
public object ExecuteAsyncScript(string script, object arguments = null, int timeout = -1) {
object args_ex = FormatArguments(arguments);
object result = session.javascript.ExecuteAsync(script, args_ex, true, timeout);
return result;
}
///
/// Waits for a piece of JavaScript to return true or not null.
///
/// Piece of JavaScript code to execute.
/// Optional arguments for the script.
/// Optional timeout in milliseconds.
/// Value not null
public object WaitForScript(string script, object arguments, int timeout = -1) {
object args_ex = FormatArguments(arguments);
object result = session.javascript.WaitFor(script, args_ex, timeout);
return result;
}
private static object FormatArguments(object arguments) {
if (arguments == null) {
return new object[0];
} else if (arguments is IEnumerable && !(arguments is string || arguments is Dictionary)) {
return arguments;
} else {
return new object[] { arguments };
}
}
#endregion
#region Find Elements
///
/// Returns the element with focus, or BODY if nothing has focus.
///
///
public WebElement ActiveElement() {
return WebElement.GetActiveWebElement(session);
}
#endregion
#region PageSource
///
/// Gets the source of the page last loaded by the browser.
///
public string PageSource() {
var result = session.Send(RequestMethod.GET, "/source");
return (string)result;
}
///
/// Returns the first occurence matching the regular expression.
///
/// The regular expression pattern to match.
/// Optional - Group number (Zero based)
/// String
public string PageSourceMatch(string pattern, short group = 0) {
const string JS = "return document.body.innerHTML.match(/{0}/)[{1}]";
string code = string.Format(JS, pattern, group);
object result = session.javascript.Execute(code, null, false);
return (string)result;
}
///
/// Returns all the occurences matching the regular expression.
///
/// The regular expression pattern to match.
/// Optional - Group number (Zero based)
/// Array of strings or null
public List PageSourceMatches(string pattern, short group = 0) {
const string JS = "var r=/{0}/g,s=document.body.innerHTML,a=[],m;"
+ "while(m=r.exec(s))a.push(m[{1}]);return a;";
string code = string.Format(JS, pattern, group);
object result = session.javascript.Execute(code, null, false);
return (List)result;
}
#endregion
#region Context
///
/// Select either the first frame on the page or the main document when a page contains iFrames.
///
/// A WebDriver instance focused on the default frame.
public void SwitchToDefaultContent() {
this.session.frame.SwitchToDefaultContent();
}
///
/// Switch focus to the specified window by name.
///
/// The name of the window to activate
/// Optional timeout in milliseconds
/// Optional - Raise an exception after the timeout when true
/// Current web driver
public Window SwitchToWindowByName(string name, int timeout = -1, bool raise = true) {
try {
return session.windows.SwitchToWindowByName(name, timeout);
} catch (Errors.NoSuchWindowError) {
if (raise)
throw new Errors.NoSuchWindowError(name);
return null;
}
}
///
/// Switch focus to the specified window by title.
///
/// The title of the window to activate
/// Optional timeout in milliseconds
/// Optional - Raise an exception after the timeout when true
/// Current web driver
public Window SwitchToWindowByTitle(string title, int timeout = -1, bool raise = true) {
try {
return session.windows.SwitchToWindowByTitle(title, timeout);
} catch (Errors.NoSuchWindowError) {
if (raise)
throw new Errors.NoSuchWindowError(title);
return null;
}
}
///
/// Switch the focus to the next window.
///
/// Optional timeout in milliseconds
/// Optional - Raise an exception after the timeout when true. Default is true.
/// Window
public Window SwitchToNextWindow(int timeout = -1, bool raise = true) {
try {
return session.windows.SwitchToNextWindow(timeout);
} catch (Errors.NoSuchWindowError) {
if (raise)
throw;
return null;
}
}
///
/// Switch the focus to the previous window
///
/// Window
public Window SwitchToPreviousWindow() {
return session.windows.SwitchToPreviousWindow();
}
///
/// Switch focus to the specified frame, by index(zero based), name or WebElement.
///
/// The name, index(zero based) or WebElement
/// Optional timeout in milliseconds
/// Optional - Raise an exception after the timeout when true
/// Current web driver
public bool SwitchToFrame(object identifier, int timeout = -1, bool raise = true) {
try {
this.session.frame.SwitchToFrame(identifier, timeout);
} catch (Errors.NoSuchFrameError) {
if (raise)
throw new Errors.NoSuchFrameError(identifier);
return false;
}
return true;
}
///
/// Select the parent frame of the currently selected frame.
///
/// The WebDriver instance focused on the specified frame.
public void SwitchToParentFrame() {
this.session.frame.SwitchToParentFrame();
}
///
/// Switch focus to an alert on the page.
///
/// Optional timeout in milliseconds
/// Optional - Raise an exception after the timeout when true
/// Focused alert
public Alert SwitchToAlert(int timeout = -1, bool raise = true) {
try {
return Alert.SwitchToAlert(session, timeout);
} catch (Errors.NoAlertPresentError) {
if (raise)
throw;
return null;
}
}
#endregion
#region Wait methods
///
/// Wait the specified time in millisecond before executing the next command
///
/// Time to wait in millisecond
public void Wait(int timems) {
SysWaiter.Wait(timems);
}
///
/// Waits for the delegate function to return not null or true
///
/// Returned object or boolean
/// Delegate taking the web driver instance as argument
/// Timeout in milliseconds
/// Variant or boolean
public T Until(Func func, int timeout = -1) {
if (timeout == -1)
timeout = session.timeouts.timeout_implicitwait;
return Waiter.WaitUntil(func, this, timeout, 80);
}
///
/// Waits for a function to return true. VBScript: Function WaitEx(webdriver), VBA: Function WaitEx(webdriver As WebDriver) As Boolean
///
/// Function reference. VBScript: wd.WaitFor GetRef(\"WaitEx\") VBA: wd.WaitFor AddressOf WaitEx)
/// Optional - Argument to send to the function
/// Optional - timeout in milliseconds
/// Current WebDriver
/// VBA example:
///
/// Sub WaitForTitle(driver, argument, result)
/// result = driver.Title = argument
/// End Sub
///
/// Sub testSimple()
/// Dim driver As New FirefoxDriver
/// driver.Get "http://www.google.com"
/// driver.Until AddressOf WaitForTitle, "Google"
/// ...
/// End Sub
///
///
///
/// VBScript example:
///
/// Function WaitForTitle(driver, argument)
/// WaitForTitle = driver.Title = argument
/// End Function
///
/// Sub testSimple()
/// Dim driver As New FirefoxDriver
/// driver.Get "http://www.google.com"
/// driver.Until GetRef("WaitForTitle"), "Google", 1000
/// ...
/// End Sub
///
///
object ComInterfaces._WebDriver.Until(object procedure, object argument, int timeout) {
if (timeout == -1)
timeout = session.timeouts.timeout_implicitwait;
return COMExt.WaitUntilProc(procedure, this, argument, timeout);
}
#endregion
#region Cache
///
/// Get the status of the html5 application cache.
///
/// {number} Status code for application cache: {UNCACHED = 0, IDLE = 1, CHECKING = 2, DOWNLOADING = 3, UPDATE_READY = 4, OBSOLETE = 5}
public CacheState CacheStatus() {
return (CacheState)session.Send(RequestMethod.GET, "/application_cache/status");
}
#endregion
#region Clipboard
///
/// Sets the text in the Clipboard
///
/// Text
public void SetClipBoard(string text) {
ClipboardExt.SetText(text);
}
///
/// Returns the text from the Clipboard
///
public string GetClipBoard() {
return ClipboardExt.GetText();
}
#endregion
}
}