C# Programming > Miscellaneous

C# Extensions for Selenium WebDriver

Selenium Extensions

Although Selenium 2 WebDriver is a powerful framework for web test automation, there is always room for improvement. Luckily for us, much of this improvement can be made by leveraging the existing functionality of Selenium WebDriver.

We have already seen how to add jQuery selectors to Selenium. Below is a list of other extensions that can be extremely useful, ranging from simple to more complex. These functions are great for writting cleaner code.

Send Keys

public static void SendKeys(this IWebElement element, string value, bool clearFirst)
{
    if (clearFirst) element.Clear();
    element.SendKeys(value);
}

This function is very simple, all it does is make our code a little from concise. In the original Selenium RC, SendKeys would clear a text field and then send the required text. In Selenium 2, text fields are not cleared first unless explicitly executed. We can then combine it to allow something like:

myElement.SendKeys("populate field", clearFirst: true);

Get Text

public static string GetText(this IWebDriver driver)
{
    return driver.FindElement(By.TagName("body")).Text;
}

This one is extremely useful and is even recommended in the Selenium FAQ. The function returns the text in the entire page without any HTML code. Note that this function can be a little quirky if the page hasn't finished loading.

Has Element

public static bool HasElement(this IWebDriver driver, By by)
{
    try
    {
        driver.FindElement(by);
    }
    catch (NoSuchElementException)
    {
        return false;
    }

    return true;
}

Selenium's default behavior for FindElement is to throw an exception if an element is not found. HasElement catches this exception to allow us to test for elements without stopping execution of the entire program.

Similary you can write an extension function for IWebElement to test of elements nested in another element.

Wait For Page To Load

This one is a tricky one. I will preface it by saying that for the most part you should not need this method. Most Selenium methods (Click, Submit, FindElement, etc) use the implicit timeout to wait for an element to appear and then wait until the page finishes loading before returning control back to your program.

Usually if you feel like you need to wait for the page to load, it is better to try adjusting the implicit wait time first or use the WaitWebDriver class.

Now, with those disclamers, sometimes it can be useful to have the ability to wait for the page to finish loading. For example, SendKeys does not wait for the page to finish loading.

public static void WaitForPageToLoad(this IWebDriver driver)
{
    TimeSpan timeout = new TimeSpan(0, 0, 30);
    WebDriverWait wait = new WebDriverWait(driver, timeout);

    IJavaScriptExecutor javascript = driver as IJavaScriptExecutor;
    if (javascript == null)
        throw new ArgumentException("driver", "Driver must support javascript execution");

    wait.Until((d) =>
    {
        try
        {
            string readyState = javascript.ExecuteScript(
            "if (document.readyState) return document.readyState;").ToString();
            return readyState.ToLower() == "complete";
        }
        catch (InvalidOperationException e)
        {
            //Window is no longer available
            return e.Message.ToLower().Contains("unable to get browser");
        }
        catch (WebDriverException e)
        {
            //Browser is no longer available
            return e.Message.ToLower().Contains("unable to connect");
        }
        catch (Exception)
        {
            return false;
        }
    });
}

Theoretically, now we can simulate a click by doing something like this (note that this works best in pre-2.6 versions of Selenium):

element.SendKeys(Keys.Enter);
driver.WaitForPageToLoad();

//Same thing as
element.Click();

Set Attribute

IWebElement objects have a handy GetAttribute function. This is useful, for example, for retrieving an input's value (which is not the same as an input's text). So why is there no SetAttribute function? The reason is simply that the creators of Selenium have strived to create a tool that simulates a user interacting with the web. A human user would not normally be able to modify the underlying attributes of an object.

Regardless, setting an element's attributes can be essential for working around some Selenium quirks. For example, masked input fields don't play well with SendKeys. In this case, we have to directly set the value of the field.

public static void SetAttribute(this IWebElement element, string attributeName, string value)
{
    IWrapsDriver wrappedElement = element as IWrapsDriver;
    if (wrappedElement == null)
        throw new ArgumentException("element", "Element must wrap a web driver");

    IWebDriver driver = wrappedElement.WrappedDriver;
    IJavaScriptExecutor javascript = driver as IJavaScriptExecutor;
    if (javascript == null)
        throw new ArgumentException("element", "Element must wrap a web driver that supports javascript execution");

    javascript.ExecuteScript("arguments[0].setAttribute(arguments[1], arguments[2])", element, attributeName, value);
}

The only downside is the web driver must have javascript enabled.

Get Attribute As Type and Text As Type

The GetAttribute function and the Text property both return strings. Many times these strings need to be parsed into other types. Thus we can write a function to wrap that functionality for us. I borrowed the generic TryParse code from StackOverflow to give the function maximum flexibility.

public static T GetAttributeAsType<T>(this IWebElement element, string attributeName)
{
    string value = element.GetAttribute(attributeName) ?? string.Empty;
    return (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(value);
}

Final Notes

I have personally found these functions very useful while coding with Selenium 2 in C#. Note that while these are implemented as extension functions, a cleaner design might implement them as part of a class that inherits a web driver class such as RemoteWebDriver or RemoteWebElement.

Back to C# Article List