WebdriverIO — Custom selenium Commands advanced concepts

Custom command is a way of extending the browser object with new functionalities with the goal of enhancing test maintenance and reducing code duplication.

WebdriverIO — Custom Commands advanced concepts

WebdriverIO provides support for browser custom commands. The goal is to bundle a specific sequence of commands, that are used frequently, in a handy single command call.

You can also override existing commands and this can be very useful if you have to provide a different behavior to an embedded WebdriverIO command.

The best place to define a custom command is within a WebdriverIO hook. onPrepare hook can be a good place if you want to integrate a 3rd party library functionality to your test.

If you have to handle API calls during your tests a good library to use is request-promise.
Example:

browser.addCommand("getUsers", function (options) {
//You can add here any other implementation you need
return
request-promise.get(options);
});

Now let’s say you have a web app that displays a loading icon each time an AJAX call occurs. It will be very difficult to handle and treat all the situations from all the locations in order to avoid errors like ‘Element is not clickable’. A good way may be to handle this by a custom command. But wait, what happens if you already wrote a lot of tests and Page Objects? Will you start to rewrite everything just to have another command: ‘browser.clickIfNoLoadingIcon’? This will be time consuming and should be avoided.

Instead let’s just override WebdriverIO click() function:

browser.addCommand("click", function (selector) {
let result;
try {
if (selector) {
result = browser.element(selector).click();
} else {
result = this.elementIdClick(this.element().value.ELEMENT);
}
} catch (err) {
// console.log('ERR CLICK');
// console.log(err.message);
if (err.message.indexOf('is not clickable') !== -1 && err.message.indexOf('LoadingIndicator') !== -1) {
// console.log('WAIT FOR LOADING');
browser.waitForLoading(10000);
result = this.click();
} else {
// console.log('THROW ERR');
throw err;
}
}
return result;
}, true);

The above is a handy custom command that will override browser.click() or element.click() commands so you’ll code will need no other changes. You’ll notice that there is a 3rd param with value ‘true’. This is needed to let WebdriverIO that we want to override an existing command, otherwise an error will be thrown (see here). browser.waitForLoading(10000); it is just another custom command that will wait for the loading icon to disappear or will throw an exception if this won’t happen within 10000ms. You have to throw an exception within waitForLoading() or the command may go into an infinite loop (NOTE: well, not quite infinite, it will end when WebdriverIO timeout occurs)

Conclusion

Augmenting the browser object with a 3rd party lib functionality is a good way to add implementation that you need within your tests and keep your code clean and maintainable.

By overriding embedded commands you can improve several aspects on your tests:

  • Test reliability. browser.pause() approach may not always work as this is defined by a static value. So in slower environments this may not be enough and needs to be increased. But increasing it is will be a disadvantage in high speed environments where the need of waiting for longer periods of time is not needed.
  • Tests performance. This is achieved in the above example by replacing any browser.pause() with static value with a dynamic way of handling the appearance or disappearance of a loading icon

Adding custom commands to WebdriverIO is a handy functionality that can help you create a better, more maintainable test code.

Here you can find the full working example used in this article.