Click here to Skip to main content
14,032,238 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

7.8K views
2 bookmarked
Posted 14 Dec 2015
Licenced Ms-PL

Create Custom Selenium IDE Export to WebDriver

, 14 Dec 2015
Rate this:
Please Sign up or sign in to vote.
Tutorial how to create custom Selenium IDE export to WebDriver tests. JavaScript code examples and explanations how to customize it for your solution.The post Create Custom Selenium IDE Export to WebDriver appeared first on Automate The Planet.

Introduction

If you are in the “test automation business”, most probably you are familiar with the Selenium IDE. Selenium provides a record/playback tool for authoring tests without learning a test scripting language (Selenium IDE). It gives you the ability to export your tests to number of popular programming languages, including Java, C#, Groovy, Perl, PHP, Python and Ruby. However, the built-in formatters cannot be customized and usually are generating not very useful code (not following your framework design). In this article, I am going to share with you how you can extend the Selenium IDE and add your custom Selenium IDE Export format template.

Create Custom Selenium IDE Export

Use Firefox Extensions

There are tons of already created Selenium IDE export plug-ins you can always see if some of them are working for you.

Selenium IDE Export Firefox Extension

Allows users to take advantage of WebDriver without having to modify their tests. You can download it from its official add-on page.

Create Custom Selenium IDE Export to WebDriver

You can add any format you like by writing JavaScript code. Follow the steps below:

  • Open Firefox and Selenium IDE
  • Open Options dialog
  • Click Formats tab
  • Create a new format by clicking “Add” button
Selenium IDE Formats Tab

­There are three empty functions- parse, format, formatCommands.

Parse Function

The parse function is almost opposite of format. This function parses the String and updates test case.

function parse(testCase, source) {
  var doc = source;
  var commands = [];
  while (doc.length > 0) {
    var line = /(.*)(\r\n|[\r\n])?/.exec(doc);
    var array = line[1].split(/,/);
    if (array.length >= 3) {
      var command = new Command();
      command.command = array[0]; command.target = array[1];
      command.value = array[2]; commands.push(command);
    }
    doc = doc.substr(line[0].length);
  }
  testCase.setCommands(commands);
}

FormatCommands Function

The formatCommands function is similar to format function. You can add additional logic to it. In my code, format will call formatCommands.

Format Function

The “format” function creates an array of commands that contain command object (Command, Target, Value).

function format(testCase, name) {
  var result = '';
  var commands = testCase.commands;
  for (var i = 0; i < commands.length; i++) {
    var command = commands[i];
    if (command.type == 'command') {
          result += command.command + ',' + command.target + ',' +     command.value + "\n";
    }
  }
  return result;    
}

Create Complete Selenium IDE Format to WebDriver C# Framework

If we have the following sample base test class, our goal might be to export our Selenium IDE tests to C# code that uses the methods provided by this base test class.

public class BaseWebDriverTest
{
    private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

    public void TestInit(IWebDriver driver, string baseUrl, int timeOut)
    {
        this.Driver = driver;
        this.BaseUrl = baseUrl;
        this.Wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeOut));
        this.TimeOut = timeOut;
    }

    public IWebDriver Driver { get; set; }

    public string BaseUrl { get; set; }

    public WebDriverWait Wait { get; set; }

    public int TimeOut { get; set; }

    public IWebElement GetElement(By by)
    {
        IWebElement result = null;
        try
        {
            result = this.Wait.Until(x => x.FindElement(by));
        }
        catch (TimeoutException ex)
        {
            log.Error(ex.Message);
            throw new NoSuchElementException(by, this, ex);
        }

        return result;
    }

    public bool IsElementPresent(By by)
    {
        try
        {
            this.Driver.FindElement(by);
            return true;
        }
        catch (NoSuchElementException ex)
        {
            log.Error(ex.Message);
            return false;
        }
    }

    public void WaitForElementPresent(By by)
    {
        this.GetElement(by);
    }
}

Below, you can find the full source code of the new Selenium IDE Export format.

var doc = '';
var newLine = "\n";
var tab = "\t";

this.configForm = '<description>Variable for Selenium instance</description>' +
                  '<textbox id="options_receiver" value="this.Browser"/>' +
                  '<description>MethodName</description>' +
                  '<textbox id="options_methodName" value="TESTMETHODNAME" />' +
                  '<description>Base URL</description>' +
                  '<textbox id="options_baseUrl" value="http://home.telerik.com"/>';

this.name = "C# Anton (Driver)";
this.testcaseExtension = ".cs";
this.suiteExtension = ".cs";
this.driver = true;

this.options = {
    receiver: "this.Browser",
    methodName: "TestMethodName",
    baseUrl: "http://automatetheplanet.com/",
    header: 'public void TestMethod()\n{\n',
    footer:
    '}\n' + newLine + newLine,
    defaultExtension: "cs"
};

function parse(testCase, source) {
    doc = source;

    var commands = [];
    var sep = {
        comma: ",",
        tab: "\t"
    };
    var count = 0;
    while (doc.length > 0) {
        var line = /(.*)(\r\n|[\r\n])?/.exec(doc);
        count++;
        var array = line[1].split(sep);
        if (array.length >= 3) {
            var command = new Command();
            command.command = array[0];
            command.target = array[1];
            command.value = array[2];
            commands.push(command);
        }
        doc = doc.substr(line[0].length);

    }
    testCase.setCommands(commands);
}

function format(testCase, name) {
    return formatCommands(testCase.commands);
}

function formatCommands(commands) {
    var result = '';
    result += this.options['header'];

    for (var i = 0; i < commands.length; i++) {
        var command = commands[i];
        if (command.type == 'command') {
            var currentResult = '';

            if (result.indexOf("\""+ "command.target" + "\"") = -1)
            {
                currentResult = setCommand(command);
                result += currentResult;
            }       
        }
    }
    result += this.options['footer'];

    return result;
}

setCommand = function (command) {
    var waitForStart = tab + "this.CurrentElement = this.GetElement((";
    var waitForEnd = "));" + newLine + tab;
    var result = tab;

    switch (command.command.toString()) {
        case 'open':
            if (command.target.toString().substring(0, 4) == "http") {
                result += options['receiver'] + 'Navigate().GoToUrl("' + command.target.toString() + '");';
            }
            else if (command.target.toString() == "/") {
                result += options['receiver'] + 'Navigate().GoToUrl(baseUrl);';
            }
            else if (isStoredVariable(command.target)) {
                result += options['receiver'] + 'Navigate().GoToUrl(' + getStoredVariable(command.target) + ');';
            }
            else {
                result += options['receiver'] + 'Navigate().GoToUrl(baseUrl + "' + command.target.toString() + '");';
            }
            break;
        case 'clickAndWait':
            result = waitForStart + searchContext(command.target.toString()) + waitForEnd;
            result += "this.CurrentElement.Click();";
            break;
        case 'click':
            result = waitForStart + searchContext(command.target.toString()) + waitForEnd;
            result += "this.CurrentElement.Click();";
            break;
        case 'type':
            result = waitForStart + searchContext(command.target.toString()) + waitForEnd;
            result += "this.CurrentElement.Clear();";
            result += newLine + tab;
            if (isStoredVariable(command.target)) {
                result += "this.CurrentElement.SendKeys(\"" + getStoredVariable(command.value) + "\");";
            }
            else {
                result += "this.CurrentElement.SendKeys(\"" + command.value + "\");";
            }
            break;
        case 'assertTextPresent':        
            result = getWaitForText(command);
            break;
        case 'verifyTextPresent':
            result = getWaitForText(command);
            break;
        case 'verifyText':
            result = getWaitForText(command);
            break;
        case 'assertText':
            result = getWaitForText(command);
            break;
        case 'waitForElementPresent':
            result = tab + 'this.WaitForElementPresent(' + searchContext(command.target.toString()) + ');';
            break;
        case 'verifyElementPresent':
            result = tab + 'this.WaitForElementPresent(' + searchContext(command.target.toString()) + ');';
            break;
        case 'verifyElementNotPresent':
            result = tab + 'this.WaitForElementNotPresent(' + searchContext(command.target.toString()) + ');';
            break;
        case 'waitForElementNotPresent':
            result = tab + 'this.WaitForElementNotPresent(' + searchContext(command.target.toString()) + ');';
            break;
        case 'waitForTextPresent':
            result = tab + 'this.WaitForTextPresent(\"' + command.target.toString() + '\");';
            break;
        case 'waitForTextNotPresent':
            result = tab + 'this.WaitForTextNotPresent(\"' + command.target.toString() + '\");';
            break;
        case 'verifyTextPresent':
            result = tab + 'this.WaitForTextPresent(\"' + command.target.toString() + '\");';
            break;
        case 'verifyTextNotPresent':
            result = tab + 'this.WaitForTextNotPresent(\"' + command.target.toString() + '\");';
            break;
        case 'waitForText':
            result = tab + 'this.WaitForText(' + searchContext(command.target.toString()) + ',\"' + command.value + '\");';
            break;
        case 'waitForChecked':
            result = tab + 'this.WaitForChecked(' + searchContext(command.target.toString()) + ');';
            break;
        case 'waitForNotChecked':
            result = tab + 'this.WaitForNotChecked(' + searchContext(command.target.toString()) + ');';
            break;
        case 'store':
            result = tab + "IJavaScriptExecutor js = (IJavaScriptExecutor) this.Driver;" + newLine;
            result += tab + 'string ' + command.value + '= ' + 'js.ExecuteScript(\"' + command.target + '\");';
            break;
        case 'storeEval':
            result = "JavascriptExecutor js = (JavascriptExecutor) this.Driver;" + newLine;
            result = tab + 'string ' + command.value + '= ' + 'js.executeScript(\"' + command.target + '\");';
            break;
        case 'storeValue':
            result = waitForStart + searchContext(command.target.toString()) + waitForEnd;
            result += 'string ' + command.value + '= ' + 'this.CurrentElement.GetAttribute("value");';
            break;
        case 'storeAttribute':
            result = waitForStart + searchContext(command.target.toString()) + waitForEnd;
            result += 'string ' + command.value + '= ' + 'this.CurrentElement.GetAttribute(\"' + getTargetAttribute(command.target) + '\");';
            break;
        case 'gotoIf':
            var strTarget = '';
            var byTarget = '';
            if (command.target.startsWith('selenium.isElementPresent')) {
                strTarget = getIfTargetElementIsPresent(command.target);
                byTarget = searchContext(strTarget);
                result = tab + 'if(' + '!IsElementPresent(' + byTarget + '))' + tab;
                result += '{' + newLine + tab;
            }
            else if (command.target.startsWith('selenium.isElementNotPresent')) {
                strTarget = getIfTargetElementIsNotPresent(command.target);
                byTarget = searchContext(strTarget);
                result = tab + 'if(' + 'IsElementPresent(' + byTarget + ')' + tab;
                result += tab + '{' + newLine + tab;
            }
            break;
        case 'selectFrame':
            result = options['receiver'] + 'SwitchTo().Frame(\"' + command.target + '\");';
            break;
        case 'label':
            result = tab + '}';
            break;
        case 'pause':
            result = tab + 'Thread.Sleep(' + command.target + ');';
            break;
        case 'echo':
            if (isStoredVariable(command.target)) {
                result = tab + 'Console.WriteLine(\"' + getStoredVariable(command.target) + '\");';
            }
            else {
                result = tab + 'Console.WriteLine(\"' + command.target + '\");';
            }
            break;
        case 'setTimeout':
            result = tab + 'timeOut= ' + command.target + ';';
            break;
        case 'select':
            result = waitForStart + searchContext(command.target.toString()) + waitForEnd;
            result += ' SelectElement selectElement = new SelectElement(this.WaitFor);'
             + newLine + tab + 'selectElement.SelectByValue(quantity);' + newLine + tab;
            break;
        default:
            result = '// The command: #####' + command.command + '##### is not supported in the current version of the exporter';
    }
    result += newLine;
    return result;
}

searchContext = function (locator) {
    if (locator.startsWith('xpath')) {
        return 'By.XPath("' + locator.substring('xpath='.length) + '")';
    }
    else if (locator.startsWith('//')) {
        return 'By.XPath("' + locator + '")';
    }
    else if (locator.startsWith('css')) {
        return 'By.CssSelector("' + locator.substring('css='.length) + '")';
    }
    else if (locator.startsWith('link')) {

        return 'By.LinkText("' + locator.substring('link='.length) + '")';
    }
    else if (locator.startsWith('name')) {

        return 'By.Name("' + locator.substring('name='.length) + '")';
    }
    else if (locator.startsWith('tag_name')) {

        return 'By.TagName("' + locator.substring('tag_name='.length) + '")';
    }
    else if (locator.startsWith('partialID')) {

        return 'By.XPath("' + "//*[contains(@id,'" + locator.substring('partialID='.length) + "')]" + '")';
    }
    else if (locator.startsWith('id')) {

        return 'By.Id("' + locator.substring('id='.length) + '")';
    }
    else {
        return 'By.Id("' + locator + '")';
    }
};

function isStoredVariable(commandValue) {

    return commandValue.substring(0, 1) == "$";
}

function getStoredVariable(commandValue) {

    return commandValue.substring(2, (commandValue.length - 1));
}

function getIfTargetElementIsPresent(commandValue) {

    return commandValue.substring(27, (commandValue.length - 2));
}

function getIfTargetElementIsNotPresent(commandValue) {

    return commandValue.substring(30, (commandValue.length - 2));
}

function getTargetAttribute(commandValue) {

    return commandValue.substring(commandValue.indexOf('@') + 1);
}

function getWaitForText(command) {
    var result = '';
    if (command.value.toString() == "") {
        result = tab + 'this.WaitForText(\"' + command.target + '\");';
    }
    else {
        result = tab + 'this.WaitForText(' + searchContext(command.target.toString()) + ',\"' + command.value + '\");';
    }
    return result;
}

There are a couple of interesting parts of the presented code. Most of the magic happens inside the setCommand function. There, through a switch statement, the function generates a string for the corresponding C# code for the used Selenium IDE command. The locator expressions are made in the searchContext.

One more thing, you can use XUL XML (XML User Interface Language) to describe how the format settings form will look like.

this.configForm = '<description>Variable for Selenium instance</description>' +
                  '<textbox id="options_receiver" value="this.Browser"/>' +
                  '<description>MethodName</description>' +
                  '<textbox id="options_methodName" value="TESTMETHODNAME" />' +
                  '<description>Base URL</description>' +
                  '<textbox id="options_baseUrl" value="http://home.telerik.com"/>';

 

After you add different text boxes and other controls, you can later use the default values set in them in your Selenium IDE Export format.

XUL XML Selenium IDE Export Format

Create Selenium IDE Export for Page Object Map Generation

As you probably know, I am a big fan of the Page Object Design Pattern. Until now, I have introduced to you a couple of different ways to use the design pattern. Usually, the most tedious part of it is the creation of the element map classes. We can ease the process with the help of Selenium IDE and a new Selenium IDE Export format. This format is going to export to a new file only the elements in the below format.

View the code on Gist.

I haven’t figured a smart way to name the generated elements because of that the first version is producing them with an increasing index suffix added to the element’s name.

Here is the full source code.

var doc = '';
var index = 1;
var tab = "\t";

this.configForm = '<description>Variable for Selenium instance</description>' +
                  '<textbox id="options_receiver" value="this.driver"/>' +
                  '<description>ClassName</description>' +
                  '<textbox id="options_className" value="PageName" />'

this.name = "C# Anton (Driver)";
this.testcaseExtension = ".cs";
this.suiteExtension = ".cs";
this.driver = true;

this.options = {
    receiver: "this.driver",
    className: "PageName",
    header: 'public partial class Page : BasePage\n{\n',
    footer:
    '}\n' + "\n" + "\n",
    defaultExtension: "cs"
};

function parse(testCase, source) {
    doc = source;

    var commands = [];
    var sep = {
        comma: ",",
        tab: "\t"
    };
    var count = 0;
    while (doc.length > 0) {
        var line = /(.*)(\r\n|[\r\n])?/.exec(doc);
        count++;
        var array = line[1].split(sep);
        if (array.length >= 3) {
            var command = new Command();
            command.command = array[0];
            command.target = array[1];
            command.value = array[2];
            commands.push(command);
        }
        doc = doc.substr(line[0].length);

    }
    testCase.setCommands(commands);
}

function format(testCase, name) {
    return formatCommands(testCase.commands);
}

function formatCommands(commands) {
    var result = '';
    result += this.options['header'];

    for (var i = 0; i < commands.length; i++) {
        var command = commands[i];
        if (command.type == 'command') {
            var currentResult = '';
            currentResult = setCommand(command);
            result += currentResult;
        }
    }
    result += this.options['footer'];

    return result;
}

setCommand = function (command) {
    var startPropElement = tab + "public IWebElement Element" + index++ + "\n" + tab + "{" + "\n" + tab + tab + "get" + "\n" + tab + tab + "{" + "\n" + tab + tab + tab;
    var endPropElement = "\n" + tab + tab + "}" + "\n" + tab + "}" + "\n";
    var result = tab;

    switch (command.command.toString()) {
        case 'clickAndWait':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'click':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'type':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'assertTextPresent':        
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'verifyTextPresent':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'verifyText':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'assertText':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'waitForElementPresent':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'verifyElementPresent':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'verifyElementNotPresent':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'waitForElementNotPresent':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'waitForTextPresent':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'waitForTextNotPresent':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'waitForChecked':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'waitForNotChecked':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'storeValue':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        case 'storeAttribute':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;      
        case 'select':
            result = startPropElement + searchContext(command.target.toString()) + endPropElement;
            break;
        default:
    }

    result += "\n";
    return result;
}

searchContext = function (locator) {
    if (locator.startsWith('xpath')) {
        return 'return this.driver.FindElement(By.XPath("' + locator.substring('xpath='.length) + '"));';
    }
    else if (locator.startsWith('//')) {
        return 'return this.driver.FindElement(By.XPath("' + locator + '"));';
    }
    else if (locator.startsWith('css')) {
        return 'return this.driver.FindElement(By.CssSelector("' + locator.substring('css='.length) + '"));';
    }
    else if (locator.startsWith('link')) {

        return 'return this.driver.FindElement(By.LinkText("' + locator.substring('link='.length) + '"));';
    }
    else if (locator.startsWith('name')) {

        return 'return this.driver.FindElement(By.Name("' + locator.substring('name='.length) + '"));';
    }
    else if (locator.startsWith('tag_name')) {

        return 'return this.driver.FindElement(By.TagName("' + locator.substring('tag_name='.length) + '"));';
    }
    else if (locator.startsWith('partialID')) {

        return 'return this.driver.FindElement(By.XPath("' + "//*[contains(@id,'" + locator.substring('partialID='.length) + "')]" + '"));';
    }
    else if (locator.startsWith('id')) {

        return 'return this.driver.FindElement(By.Id("' + locator.substring('id='.length) + '"));';
    }
    else
    {
        return 'return this.driver.FindElement(By.Id("' + locator + '"));';
    }
};

So Far in the 'Pragmatic Automation with WebDriver' Series

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Anton Angelov
CEO Automate The Planet
Bulgaria Bulgaria
Anton Angelov is an IT Consultant and Quality Assurance Architect at Innovative Lab. He is passionate about automation testing and designing test harness and tools, having the best industry development practices in mind. In addition, he is an active blogger and the founder of Automate The Planet. He strives to make the site one of the leading authorities in Automation Testing by presenting compelling articles, inspiring ardent discussions amongst the community. He is also one of the most-rated-answer authors of questions about Test Automation Frameworks (WebDriver) on Stack Overflow.

You may also be interested in...

Pro

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06 | 2.8.190419.4 | Last Updated 14 Dec 2015
Article Copyright 2015 by Anton Angelov
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid