After the last post, I continued my Nightwatch exploration with the Page Objects and the command production.
Get back where we left the boilerplate project nightwatch-boilerplate which can be found already fulfilled with the updates of this article on the branch page-object-assertion-command.
Start by adding in the configuration's root file the paths to the folders (we have previously created) so that Nightwatch can find each part:
[...] "page_objects_path": "nw/pages", "custom_commands_path" : ["nw/commands"], "custom_assertions_path" : ["nw/assertions"], [...]
Then, carry on the Page Objects
Object page
First of all, reminder: the page object pattern defines an abstraction which allow to attach to a part of a page, an object which will be responsible for its driving. This approach has many advantages. Among many things, this allows you to write your CSS selectors only once and then avoid code repetitions. Also, tests become a lot more readable by exposing some business methods (Ex: browser.page.google().search) instead of technical methods (Ex: browser.click('button[name=btnG]')[ …]).
In practice, we will create a file nw/pages/google.js describing the page object responsible for the Google research page:
module.exports = {
elements: {
searchInput: 'input[type=text]',
searchBtn: 'button[name=btnG]'
},
commands: [{
fillInSearchInput () {
this
.waitForElementVisible('body')
.setValue('@searchInput', this.api.globals.searchTerm);
return this.api;
},
submit () {
this
.waitForElementVisible('@searchBtn')
.click('@searchBtn')
.api.pause(1000);
return this.api;
}
}]
};
The first part 'element', creates aliases bounded to CSS selectors, the second part 'command' allows to create the business methods used in our tests. Thanks to the usage of aliases (Ex: '@searchInput') in the methods, we do not repeat the CSS selectors and avoid the error threat.
Tips: Personally, I usually use return 'this.api' in the commands because 'this.api' matches the 'browser' parameter in the tests. By returning it, we can chain calls of different page object.
Now, the test nw/tests/researchOnGoogle.test.js looks like that:
module.exports = {
'Search on google': (browser) => {
browser
.init()
.page.google().fillInSearchInput()
.page.google().submit()
.assert.containsText('#main', browser.globals.movieName)
.end();
},
after: (browser) => {
browser.end();
}
};
It is much readable, Is it not? (see: old version)
Thanks to our google.js file in the page objects directory, we can access to a method google which gives access to a page object with the same name.
Command
In this part, I will explain you how to build more complex commands with use of the prototype. This command format allows to handle, among other things, the asynchronism. If our need is relatively simple and synchronous, stay on the simple format shown in the doc.
Like a page object, to create a command you have to create a file in the folder dedicated to this usage. Let's create this file nw/commands/longCommand.js with this content:
const util = require('util');
const events = require('events');
const LONG_TIME = 2000;
function Cmd() {
events.EventEmitter.call(this);
}
util.inherits(Cmd, events.EventEmitter);
Cmd.prototype.command = function () {
setTimeout(() => this.emit('complete'), LONG_TIME);
return this;
};
module.exports = Cmd;
So, a command in Nightwatch is a prototype inheriting of EventEmitter and which contains a method 'command'.
To signal to Nightwatch that this command is over, we must dispatch the 'complete' event, and then to be chainable we return 'this'. After integrating it in the test we obtain this final version:
module.exports = {
'Search on google': (browser) => {
browser
.init()
.longCommand()
.page.google().fillInSearchInput()
.page.google().submit()
.assert.containsText('#main', browser.globals.movieName)
.end();
},
after: (browser) => {
browser.end();
}
};
Conclusion
This article, with the previous one covering the same topic, you are now ready to write efficient, readable, and easy to maintain acceptable tests, and reduce the execution time in the same time.
Matthew Breton CTO at JS Republic
[separator type=”” size=”” icon=”star”] [actionbox color=”default” title=”” description=”JS-REPUBLIC is a service company specializing in JavaScript development. We are an approved training center. Find all our technical training on our partner site dedicated to Training” btn_label=”Our training” btn_link=”http://training.ux-republic.com” btn_color=”primary” btn_size=”big” btn_icon=”star” btn_external =”1″]
