Waiting in WebDriverJs

I have been trying to work out how to wait elegantly for dynamic content when using WebDriverJs.

I set up an example page to has dynamic content to explain these examples clearly: webdriverjsdemo.github.io

The best way to solve problems is to break them down into small, clean, reproducible examples.

Waiting for an element to appear

This is the easiest thing to wait for, since it’s being added to the DOM, and WebDriverJS provides a neat way to do this using driver.wait, and the until class.

var webdriver = require('selenium-webdriver');
var assert = require('assert');
var until = webdriver.until;
var by = webdriver.By;

var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
driver.get('http://webdriverjsdemo.github.io');
driver.wait(until.elementLocated(by.id('elementappearschild')), 10000, 'Could not locate the child element within the time specified');
driver.findElement(by.id('elementappearschild')).getText().then(function(text) {
   assert.equal(text, 'Boo!');
});
driver.quit();

Waiting for an element to disappear

This is a little bit tricker to do. If we try to use the until.elementIsNotVisible function:

var webdriver = require('selenium-webdriver');
var assert = require('assert');
var until = webdriver.until;
var by = webdriver.By;

var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
driver.get('http://webdriverjsdemo.github.io');
var element = driver.findElement(by.id('elementdisappears'));
driver.wait(until.elementIsNotVisible(element),10000);
driver.isElementPresent(by.id('elementdisappears')).then(function(present) {
   assert.equal(present, false);
});
driver.quit();

Then we get a:

StaleElementReferenceError: stale element reference: element is not attached to the page document

when the element is removed as we have a reference to it. We need to be a little more clever:

var webdriver = require('selenium-webdriver');
var assert = require('assert');
var until = webdriver.until;
var by = webdriver.By;

var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
driver.get('http://webdriverjsdemo.github.io');
driver.wait(function() {
    return driver.isElementPresent(by.id('elementdisappears')).then(function(present) {
        return !present;
    });
}, 10000, 'The element was still present when it should have disappeared.');
driver.isElementPresent(by.id('elementdisappears')).then(function(present) {
   assert.equal(present, false);
});
driver.quit();

Since we’re using driver.isElementPresent we are fine as we don’t have references to the DOM. Note that this function call needs to use the value of the promise to return the result.

Waiting for an element’s text to change to a value

This is a bit easier, as whilst we have a reference to a DOM element, only its text shall change, so we can use until.elementTextContains to wait for it to change:

var webdriver = require('selenium-webdriver');
var assert = require('assert');
var until = webdriver.until;
var by = webdriver.By;

var driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
driver.get('http://webdriverjsdemo.github.io');
var element = driver.findElement(by.id('elementchangestext'));
driver.findElement(by.id('elementchangestext')).getText().then(function(text) {
    assert.equal(text, 'old');
});
driver.wait(until.elementTextContains(element, 'new'),10000);
driver.findElement(by.id('elementchangestext')).getText().then(function(text) {
    assert.equal(text, 'new');
});
driver.quit();

Summary

Whilst waiting in WebDriverJs can seem complicated, once you start to understand it and use driver.wait and the until class to your advantage it becomes pretty easy. If you ever need to you can revert back to using a wait function instead of the until class which is simpler but doesn’t always suit your purpose.

Author: Alister Scott

Alister is an Excellence Wrangler for Automattic.