Introduction: Web Driver IO Tutorial Using a Live Web Site and Working Examples

About: Passionate about Technology, online payments, online security, Lighting Industry, Q/A automation and Arduino based projects.

Web Driver IO Tutorial Using A Live Web Site And Working Examples

Last Update: 07/26/2015

(Check back often as I update this instructables with more details and examples)

Background

I recently had an interesting challenge presented to me. I needed to introduce automated testing to a Q/A department with very little technical experience and no programming background.

This was really two (2) separate challenges. The first was to identify the technologies to perform the automated testing. The second was to train the Q/A department.

The article will only address the technologies used and what I learned in the process.

The technologies worked well but I really had to search for information and spent many hours figuring out issues.

I had a hard time finding information on the Internet about these technologies all working together.

I wanted to share this information, so I wrote this article along with working example test scripts and a test web site to run the scripts against.

All test scripts can be found on github and the working test site is located at Web Driver IO Tutorial Test Site

I hope you find it useful. If you do, please let me know.

Objectives
Use technologies to:

  • Test web site functionality
  • Test JavaScript functionality
  • Can be run manually
  • Can be run automatically
  • Easy to learn language for non programmers
    • Q/A personnel with basic knowledge of HTML and JavaScript
  • Use Open source software only

Technologies

List of technologies I choose:

  • mocha – test runner - executes the test scripts
  • shouldjs – assertion library
  • webdriverio – browser control bindings (language bindings)
  • selenium – browser abstraction and running factory
  • Browser/Mobile drivers + browsers
    • Firefox (Browser only)
    • Chrome (Browser and driver)
    • IE (Browser and driver)
    • Safari (Browser and driver plug-in)

Step 1: Software Installation

To get started you need to have Node JS, Web Driver IO, Mocha, Should and Selenium stand alone server installed.

Here are installation instructions for Windows 7.

(I'm a Mac/Linux user but I had to install everything on Windows 7 machines, this is why I have included it for your reference. The procedure for installing on a Mac/Linux is similar. Please consult with online references for more information.)

From a browser:

  • Install Node which includes NPM (Node Package Manager)
  • go to https://nodejs.org/
    • Click install
    • Save and run file
    • Set the path and variable (NODE_PATH)
    • Go to Control Panel->System and Security->System
      • Advanced System Settings
      • Environment Setting (User variables)
        • Add to PATH
          • C:\Users\{USERNAME}\AppData\Roaming\npm;
        • Add the NODE_PATH (System variables)
          • C:\Users\{USERNAME}\AppData\Roaming\npm\node_modules

Note: I installed all software below using the npm global option (-g). This is normally not recommended but for this installation I needed to install globally since it would be used across multiple projects.

Open command prompt (cmd):

(local user administrator)

  • Install selenium "web driver IO"
    • npm install webdriverio -g
      • This will install web driver IO globally on your machine
  • Install “mocha” test runner software
    • npm install mocha -g
      • This will install mocha globally on your machine
  • Install “should” assertion library
    • npm install should -g
      • This will install “should” globally on your machine
  • Install Selenium Stand Alone Server
  • Install browsers and browser drivers to test with
    • From cmd prompt:
      • Create “selenium” directory
      • C:\Users\{USERNAME}\selenium
      • Commands:
        • cd C:\Users\{USERNAME}
        • mkdir selenium
      • Firefox
        • Install firefox browser, if not already installed.
        • The path has to be set to start Firefox from command prompt (cmd).
        • Control Panel->System and Security->System
          • Advanced System Settings
          • Environment Settings
          • Add (append use semi-colon) to Path Variable
          • C:\Program Files (x86)\Mozilla Firefox
        • No special web driver needed for Firefox.
      • Chrome
        • Install chrome browser, if not already installed.
        • The path MAY be set to start Chrome from command prompt (cmd).
        • Test first: chrome.exe from command prompt (cmd)
        • If chrome doesn't start then:
        • Control Panel->System and Security->System
          • Advanced System Settings
          • Environment Settings
          • Add (append use semi-colon) to Path Variable
          • C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
        • A special web driver is needed for Chrome.
          • Go to chromium.org and unzip 32 bit driver into the “selenium” directory.
      • Internet Explorer (for Windows only - will not work on other platforms)

Step 2: Basic Test Script

Let's start with some basics.

Here is a simple mocha script that will open a web site and verify the title.

// tutorial1.js
//
// This is a simple test script to open a website and
// validate title.
// required libraries
var webdriverio = require('webdriverio'),
  should = require('should');

// a test script block or suite
describe('Title Test for Web Driver IO - Tutorial Test Page Website', function() {
// set timeout to 10 seconds
 this.timeout(10000);
  var driver = {};

  // hook to run before tests
  before( function (done) {
    // load the driver for browser
    driver = webdriverio.remote({ desiredCapabilities: {browserName: 'firefox'} });
    driver.init(done);
  });

  // a test spec - "specification"
  it('should be load correct page and title', function () {
    // load page, then call function()
    return driver
      .url('http://www.tlkeith.com/WebDriverIOTutorialTest.html')
      // get title, then pass title to function()
      .getTitle().then( function (title) {
        // verify title
        (title).should.be.equal("Web Driver IO - Tutorial Test Page");
        // uncomment for console debug
        // console.log('Current Page Title: ' + title);
      });
  });

 // a "hook" to run after all tests in this block
 after(function(done) {
    driver.end(done);
  });
});

Observations:

  • You should first notice the test script is written in JAVASCRIPT (ends in .js extension).
  • The basic structure is almost identical for all test scripts.
    • Header Comments (//)
    • Required Libraries
    • Set Options (optional)
    • Hook: Load Browser Driver
    • Test Suite (describe)
    • Test Specs (can be many Specs in a Suite)
    • Hook: Clean up
  • The test suite begins with a describe function which takes two parameters:
    • String - Description of test suite
      • “Check page for proper verbiage”
      • “Verify radio button operations”
    • function - block of code to execute
      • describe(‘Description of test suite', function() { });
  • The test suite will contain 1 or more test spec (specification)
  • Specs begin with it function which takes two parameters:
    • String - Description of test specification
      • “Should be correct link text and link URL"
      • “Should contain correct verbiage (copy deck)
    • function - block of code to execute
    • it(‘Description of test specification', function() { });
  • A spec contains one or more expectations that test the state of the code
  • These are called assertions
    • The “should” library provides the assertions
  • In almost all cases, you will need to locate one or more elements using a selector then perform some operation on the element(s)
    • Examples:
      • Find text on a page and verify the text
      • Populate a form and submit
      • Verify CSS of an element

Let's take a closer look at the example with comments.

Load the required libraries: web driver IO and should.

// required libraries
var webdriverio = require('webdriverio'),
  should = require('should'); 

Define the test suite. This suite it is called: "Title Test for Web Driver IO - Tutorial Test Page Website"

// a test script block or suite
describe('Title Test for Web Driver IO - Tutorial Test Page Website', function() {
...
});

Set the timeout to 10 seconds so the script doesn't timeout when loading the page.

// set timeout to 10 seconds
  this.timeout(10000); 

Hook to load the browser driver before running the specifications "specs". The Firefox driver is loaded in this example.

// hook to run before tests
before( function (done) {
  // load the driver for browser
  driver = webdriverio.remote({ desiredCapabilities: {browserName: 'firefox'} });
  driver.init(done);
}); 

Define the test specification.

// a test spec - "specification"
it('should be load correct page and title', function () {
  ...
}); 

Load the website page

.url('http://www.tlkeith.com/WebDriverIOTutorialTest.html') 

Get title, then pass title to function()

.getTitle().then( function (title) {
  ...
}); 

Verify the title using the should assertion library.

(title).should.be.equal("Web Driver IO - Tutorial Test Page"); 

Hook to quit and cleanup the driver when finished.

 // a "hook" to run after all tests in this block
after(function(done) {
  driver.end(done);
});

Step 3: Run the Test Script

Now let's see what the test script does when it is ran.

First start the Selenium Stand Alone Server:

  • For Windows use command line (cmd):
    • java -jar <jar filename>
    • # java -jar selenium-server-standalone-2.46.0.jar
  • For Mac or Linux, open terminal:
    • java -jar <jar filename>
    • $ java -jar selenium-server-standalone-2.46.0.jar
  • See screenshot above

Next run the test script:

  • For Windows use command line (cmd):
    • mocha
    • # mocha tutorial1.js
  • For Mac or Linux, open terminal:
    • mocha
    • $ mocha tutorial.js
  • See screenshot above

So what happened?

Mocha invokes the script "tutorial1.js". The driver started the browser (Firefox), loaded the page and verified the title.

Step 4: Example Web Site

All the examples are run against this site.

The example web site is located at: Web Driver IO Tutorial Test Page

All test scripts can be downloaded from github.

Step 5: Specific Examples

All code is available on github: Web Driver IO Tutorial on github

  • Verify Link and Link Text in an unordered list - "linkTextURL1.js"
    • The unordered list has an id="mylist" and the link is the 4th list item.
    • The URL should be "http://tlkeith.com/contact.html"
  // Verify Contact Us link text
  it('should contain Contact Us link text', function () {
    return driver
      .getText("//ul[@id='mylist']/li[4]/a").then(function (link) {
        console.log('Link found: ' + link);
        (link).should.equal("Contact Us");
      });
  });

  // Verify Contact Us URL
  it('should contain Contact Us URL', function () {
    return driver
      .getAttribute("//ul[@id='mylist']/li[4]/a", "href").then(function (link) {
        (link).should.equal("http://tlkeith.com/contact.html");
        console.log('URL found: ' + link);
      });
  });
  • Verify Copyright Text - "Copyright1.js"
    • The copyright is in the footerThis example shows 2 different ways to locate the copyright text:
      • by the id="copyright" as the element selector
      • by using xpath as the element selector
  // Verify Copyright text using id as element selector
  it('should contain Copyright text', function () {
    return driver
      .getText("#copyright").then(function (link) {
        console.log('Copyright found: ' + link);
        (link).should.equal("Tony Keith - tlkeith.com @ 2015 - All rights reserved.");
      });
  });

  // Verify Copyright text using xpath as element selector
  it('should contain Copyright text', function () {
    return driver
      .getText("//footer/center/p").then(function (link) {
        console.log('Copyright found: ' + link);
        (link).should.equal("Tony Keith - tlkeith.com @ 2015 - All rights reserved.");
      });
  });
  • Populate Form Fields and Submit - "formFillSubmit1.js"
    • Fill in the first name, last name and submit, then wait for results.
    • This example shows 3 methods of filling the first name input field:
      • by id
      • by xpath from input
      • by xpath from form->input
    • Also shows how to clear an input field
  // Set the first name using id to: Tony
  it('should set first name to Tony', function () {
    return driver.setValue("#fname", "Tony")
      .getValue("#fname").then( function (e) {
        (e).should.be.equal("Tony");
        console.log("First Name: " + e);
      });
  });

  // Clear the first name using id
  it('should clear first name', function () {
    return driver.clearElement("#fname")
      .getValue("#fname").then( function (e) {
        (e).should.be.equal("");
        console.log("First Name: " + e);
      });
  });

  // Set the first name using xpath from input to: Tony
  it('should set first name to Tony', function () {
    return driver.setValue("//input[@name='fname']", "Tony")
      .getValue("//input[@name='fname']").then( function (e) {
        (e).should.be.equal("Tony");
        console.log("First Name: " + e);
      });
  });

  // Clear the first name using xpath from input
  it('should clear first name', function () {
    return driver.clearElement("//input[@name='fname']")
      .getValue("//input[@name='fname']").then( function (e) {
        (e).should.be.equal("");
        console.log("First Name: " + e);
      });
  });

  // Set the first name using xpath from form to: Tony
  it('should set first name to Tony', function () {
    return driver.setValue("//form[@id='search-form']/input[1]", "Tony")
      .getValue("//form[@id='search-form']/input[1]").then( function (e) {
        (e).should.be.equal("Tony");
        console.log("First Name: " + e);
      });
  });

  // Set the last name using id to: Keith
  it('should set last name to Keith', function () {
    return driver.setValue("#lname", "Keith")
      .getValue("#lname").then( function (e) {
        (e).should.be.equal("Keith");
        console.log("Last Name: " + e);
      });
  });

  // Submit form and wait for search results
  it('should submit form and wait for results', function () {
    return driver.submitForm("#search-form").then( function(e) {
      console.log('Submit Search Form');
      })
      .waitForVisible("#search-results", 10000).then(function (e) {
        console.log('Search Results Found');
      });
  });
  • Click Show/Hide Button and Verify Text - "showHideVerify1.js"
    • The text is in a show/hide element. The button controls the state.
    • This example shows:
      • Click the button to expand
      • Wait for the element to be visible (expanded)
      • Verify text (copy deck)
  // click "More Info" button and verify text in expanded element
  it('should click more info button and verify text', function () {
    return driver
      .click("#moreinfo").then (function () {
        console.log('Clicked More Info button');
      })
      .waitForVisible("#collapseExample", 5000)
      .getText("//div[@id='collapseExample']/div").then (function (e) {
        console.log('Text: ' + e);
        (e).should.be.equal("All things good go here!");
       });
  });
  • Validate Form Field Errors - "formFieldValidation.js"
    • Use test scripts to verify correct error messages are produced.
    • This example shows:
      • Verify the error text messages and verify location (unordered list position).
it('should contain 5 errors: first/last/address/city/state', function () {
  return driver
     .getText("//ul[@class='alert alert-danger']/li[1]").then(function (e) {
        console.log('Error found: ' + e);
        (e).should.be.equal('Please enter first name');
      })
      .getText("//ul[@class='alert alert-danger']/li[2]").then(function (e) {
        console.log('Error found: ' + e);
        (e).should.be.equal('Please enter last name');
      })
      .getText("//ul[@class='alert alert-danger']/li[3]").then(function (e) {
        console.log('Error found: ' + e);
        (e).should.be.equal('Please enter address');
      })
      .getText("//ul[@class='alert alert-danger']/li[4]").then(function (e) {
        console.log('Error found: ' + e);
        (e).should.be.equal('Please enter city');
      })
      .getText("//ul[@class='alert alert-danger']/li[5]").then(function (e) {
        console.log('Error found: ' + e);
        (e).should.be.equal('Please enter state');
      });
  });
  • Looping Data to Validate URL Link/Text/Page - "LoopDataExample1.js"
    • This example shows:Use an array of JSON data to store the link and name, then iterate
      • Verify each URL text and link
      • Click link and load page
// Link data - link and text
var linkArray = [
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/tutorial1.js", "name" : "tutorial1.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/linkTextURL1.js", "name" : "linkTextURL1.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/copyright1.js", "name" : "copyright1.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/formFillSubmit1.js", "name" : "formFillSubmit1.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/showHideVerify1.js", "name" : "showHideVerify1.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/dynamicBrowser.js", "name" : "dynamicBrowser.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/callbackPromise.js", "name" : "callbackPromise.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/debugExample1.js", "name" : "debugExample1.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/formFieldValidation.js", "name" : "formFieldValidation.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/common/commonLib.js", "name" : "commonLib.js"},
{"link" : "https://github.com/onewithhammer/WebDriverIOTutorial/blob/master/dataLoopExample1.js", "name" : "dataLoopExample1.js"}
];
...
 // loop through each linkArray 
  linkArray.forEach(function(d) {
    it('should contain text/link then goto page - ' + d.name, function() {
      return driver
      // make sure you are on the starting page
      .url('http://www.tlkeith.com/WebDriverIOTutorialTest.html')
      .getTitle().then( function (title) {
        // verify title
        (title).should.be.equal("Web Driver IO - Tutorial Test Page");
      })
      // find the URL
      .getAttribute('a=' + d.name, "href").then(function (link) {
        (link).should.equal(d.link);
        console.log('URL found: ' + d.link);
      })
      // go to URL page and verify it exists
      .click('a=' + d.name)
      .waitForVisible("#js-repo-pjax-container", 10000).then(function () {
        console.log('Github Page Found');
      });
    });
  });
  • Looping Static Data to Populate Form Fields - "loopDataExample2.js"
    • This example shows:Use an array of JSON data to store first/last name
      • Loop through the data to populate form fields, then submit the form
      • Wait for results page
      • Verify first / last name on the results page
// data array - firstName and lastName<br>var dataArray = [
{"firstName" : "Tony", "lastName" : "Keith"},
{"firstName" : "John", "lastName" : "Doe"},
{"firstName" : "Jane", "lastName" : "Doe"},
{"firstName" : "Don", "lastName" : "Johnson"}
];

...

  // loop through each dataArray <br>  dataArray.forEach(function(d) {
    it('should populate fields, sumbit page', function() {
      return driver
      // make sure you are on the starting page
      .url('http://www.tlkeith.com/WebDriverIOTutorialTest.html')
      .getTitle().then( function (title) {
        // verify title
        (title).should.be.equal("Web Driver IO - Tutorial Test Page");
      })
      .setValue("#fname", d.firstName)
      .getValue("#fname").then( function (e) {
        (e).should.be.equal(d.firstName);
        console.log("First Name: " + e);
      })
      .setValue("#lname", d.lastName)
      .getValue("#lname").then( function (e) {
        (e).should.be.equal(d.lastName);
        console.log("Last Name: " + e);
      })
      .submitForm("#search-form").then( function() {
        console.log('Submit Search Form');
      })
      .waitForVisible("#search-results", 10000).then(function () {
        console.log('Result Page Found');
      })
      .getText("//h1").then(function (link) {
        console.log('Text found: ' + link);
        (link).should.equal("Welcome " + d.firstName + " " + d.lastName + ".");
      });
    });
  });
  • Validate CSS Properties - "cssValidation1.js"
    • This example shows how to:
      • Validate the following CSS properties:
        • color
        • padding (top, bottom, right, left)
        • background color
it('should contain correct color of error text', function () {<br>    return driver
     .getCssProperty("//ul[@class='alert alert-danger']/li[1]", "color").then(function (result) {
        console.log('Color found: ' + result.parsed.hex + " or " + result.value);
        (result.parsed.hex).should.be.equal('#a94442');
      });
  });
  it('should contain correct padding in table cell', function () {
    return driver
      // padding: top right bottom left
      .getCssProperty("//table[@id='filelist']/thead/tr[1]/td[1]", "padding-top").then(function (result) {
        console.log('padding-top found: ' + result.value);
        (result.value).should.be.equal('10px');
      })
      .getCssProperty("//table[@id='filelist']/thead/tr[1]/td[1]", "padding-bottom").then(function (result) {
        console.log('padding-bottom found: ' + result.value);
        (result.value).should.be.equal('10px');
      })
      .getCssProperty("//table[@id='filelist']/thead/tr[1]/td[1]", "padding-right").then(function (result) {
        console.log('padding-right found: ' + result.value);
        (result.value).should.be.equal('5px');
      })
      .getCssProperty("//table[@id='filelist']/thead/tr[1]/td[1]", "padding-left").then(function (result) {
        console.log('padding-left found: ' + result.value);
        (result.value).should.be.equal('5px');
      });
  });
 it('should contain correct background color in table header', function () {
    return driver
     .getCssProperty("//table[@id='filelist']/thead", "background-color").then(function (result) {
        console.log('background color found: ' + result.parsed.hex);
        (result.parsed.hex).should.be.equal('#eeeeee');
      });
  });

Step 6: Tips and Tricks

  • Debugging:
    • Turn on logging at the driver level for more debug and to create logs.
      • Set logLevel to 'verbose'
      • Set logOutput to directory name ('logs')
driver = webdriverio.remote(loglevel: 'verbose', logOutput: 'logs', {desiredCapabilities: {browserName: 'firefox'} });<br>
  • Use console.log(), debug(), getText() to debug.
    • console.log() - Use to display information to determine state.
    • debug() - Use pause browser/script until enter is pressed on command line.
    • getText() - Use to verify you are interacting with the correct element.
      • Especially helpful with xpath expressions.
  // Click on the Item 3 from list
  it('should click on Item 3 from list', function () {
    // use getText() to verify the xpath is correct for the element
    return driver
      .getText("//ul[@id='mylist']/li[3]/div/div/a").then(function (link) {
        // use console.log() to output information
        console.log('Link found: ' + link);
        (link).should.equal("Item 3");
      })
      // use debug() to stop action to see what is happening on the browser
      .debug()
      .click("//ul[@id='mylist']/li[3]/div/div/a").then (function () {
        console.log('Link clicked');
      })
      // wait for google search form to appear
      .waitForVisible("#tsf", 20000).then(function (e) {
        console.log('Search Results Found');
      });
  });
  • Use Environment Variable to Change the Browser Dynamically:
    • Use environment variable SELENIUM_BROWSER to invoke a different browser without modifying the test script each time.
    • Requires a slight coding change to support.

Code Changes:

// load the driver for browser
driver = webdriverio.remote({ desiredCapabilities: 
  {browserName: process.env.SELENIUM_BROWSER || 'chrome'} });

Supported Browsers:

  • Internet Explorer - IE 8+ (Windows Only)
    • SELENIUM_BROWSER=ie mocha
  • Firefox 10+ (Windows/Max/Linux)
    • SELENIUM_BROWSER=firefox mocha
  • Chrome 12+ (Windows/Max/Linux)
    • SELENIUM_BROWSER=chrome mocha
  • Opera 12+
    • SELENIUM_BROWSER=opera mocha
  • Safari
    • SELENIUM_BROWSER=safari mocha

Testing:

  • For Windows use git bash shell:
    • SELENIUM_BROWSER=chrome mocha
    • $ SELENIUM_BROWSER=chrome mocha DynamicBrowser.js
  • For Mac or Linux, open terminal:
    • SELENIUM_BROWSER=chrome mocha
    • $ SELENIUM_BROWSER=chrome mocha DynamicBrowser.js
  • Responsive Testing:
    • Determine breakpoints based on project or framework (ie bootstrap).
    • Define environment variables for each breakpoint:
      • DESKTOP - 1200 px
      • TABLET - 992 px
      • MOBILE - 768 px
    • Develop a reusable command to read the environment variable and set the browser size.
      • See example below.
    • Call the reusable command in your test script.
  // reusable code - library<br>  // code snippet
  if(bp == "DESKTOP") {
    obj.width = 1200;
    obj.height = 600;
    obj.name = bp;
  }
  else if(bp == "TABLET") {
    obj.width = 992;
    obj.height = 600;
    obj.name = bp;
  }
  else if(bp == "MOBILE") {
    obj.width = 768;
    obj.height = 400;
    obj.name = bp;
  }
  // Test script
  before( function(done) {
    winsize = common.getWindowSizeParams();
    ...
    driver.addCommand('setWindowSize', common.setWindowSize.bind(driver));
  }

  // set the window size
  it('should set window size', function (done) {
    // only the width matters
    driver.setWindowSize(winsize.width, winsize.height, function () {}).call(done);
  });
  • Reusable Commands (Custom Commands):
    • Web Driver IO is easily extendable.
    • I like to put all reusable commands into a library. (maybe this is old school but it works!)

common/commonLib.js

//  verifyLastNameCheckError()
//
//    Description:
//      Verifies the last name form validation error message
//
//    Input:
//      number - index of error (1-5)
//    Output:
//      none
//
var verifyLastNameCheckError = function () {
    var idx = arguments[0],
    callback = arguments[arguments.length - 1];
    this
        .getText("//ul[@class='alert alert-danger']/li[" + idx + "]", function(err, e) {
            console.log('Error found: ' + e);
            (e).should.be.equal('Please enter last name');
        })
        .call(callback);
};
// export the function
module.exports.verifyLastNameCheckError = verifyLastNameCheckError;

Here are the specific changes needed to call a reusable function

See formFieldValidation.js for complete working example

  // require the reusable command - CommonLib
  common = require('./Common/CommonLib');
  ...

  // bind the commands
  driver.addCommand('verifyFirstNameError', common.verifyFirstNameCheckError.bind(driver));
  driver.addCommand('verifyLastNameError', common.verifyLastNameCheckError.bind(driver));

  it('should contain 2 errors: first/last name', function () {
    // call the reusable function
    driver
      .verifyFirstNameError(1);
      .verifyLastNameError(2);
  });
  • Project File/Directory Structure:
    • Here is typical project structure:
      • "Project" - main project directory
        • README.md - readme for global project
        • "Common" - directory for global functions common to all projects
          • common-lib.js - global function library
          • README.md - readme for global functions
        • "Product1" - directory for product 1
          • test-script1.js
          • test-script2.js
          • "Common" - directory for local functions to project 1
            • prod1-lib.js - local function library for project 1
            • README.md - readme for local functions to project 1
        • "Product2" - directory for product 2test-script1.jstest-script2.js
          • "Common" - directory for local functions to project 2
            • prod2-lib.js - local function library for project 2
            • README.md - readme for local functions to project 2
  • Break test scripts into multiple files:
    • Here is a sample of using multiple files:
      • Sanity Check - basic test script to verify everything is working
      • Static Element and Text Validation - verify all elements and text
      • Form/Page Error Validation - error validation
      • Search Results - test dynamic content
  • Callbacks VS. Promises:
    • Version 3 of Web Driver IO supports both callbacks and promises.

      Promises are the preferred method since it reduces the error handling
      code. Please see below the same example written using callbacks and promises.

Callbacks

  // Set/verify first/last name using Callback
  it('should set/verify first/last name using Callbacks', function (done) {
    driver.setValue("#fname", "Tony", function (e) {
      driver.getValue("#fname", function (err, e) {
        (e).should.be.equal("Tony");
        console.log("First Name: " + e);

        driver.setValue("#lname", "Keith", function (e) {
          driver.getValue("#lname", function (err, e) {
            (e).should.be.equal("Keith");
            console.log("Last Name: " + e);
            done();
          });
        });
      });
    });
  });

Promises

// Set/verify first/last name using Promises
it('should set/verify first/last name using Promises', function () {
    return driver.setValue("#fname", "Tony")
      .getValue("#fname").then( function (e) {
        (e).should.be.equal("Tony");
        console.log("First Name: " + e);
      })
      .setValue("#lname", "Keith")
      .getValue("#lname").then( function (e) {
        (e).should.be.equal("Keith");
        console.log("Last Name: " + e);
      });
});

Step 7: More Resources

Here are some additional resources for your reference:

Step 8: Conclusion

I spent some time researching the technologies to use for my project. I originally started with Selenium Web Driver but switched to using Web Driver IO. Web Driver IO seemed to be easier to use and much easier to extend (at least the documentation for extending - reusable commands was better).

When I first started looking at the technologies it was hard to find good examples that were relative to anything I was trying to do. This is the reason I wanted to share this information and knowledge with you.

The technologies worked much better than I expected however there was learning curve involved. Once I understood how all the components worked together, I was able to write complicated test scripts in a very short time. The most difficult scripts were JavaScript based components such as a date picker and modal selectors.

I have never labeled myself as a JavaScript developer nor did I every want to be JavaScript expert, but using these technologies has definitely motivated me to sharpen my JS skills.

I hope this article is useful and the examples are clear and informative.

Please let me know if you have any questions or comments.

Thank you,

Tony Keith