After last week's article, I continue my Nightwatch exploration with the Object Pages, and the realization of command.
Let's pick up where we left off with the management nightwatch-boilerplate which you can find already completed with the topics of this article on the branch page-object-assertion-command.
Let's start by adding to the root of the configuration file the paths to the folders (which we will have created beforehand) so that Nightwatch can find each part:
[...] "page_objects_path": "nw/pages", "custom_commands_path" : ["nw/commands"], "custom_assertions_path" : ["nw/assertions"], [...]
And, let's go directly to the Page object
Object page
As a reminder, the Page Object pattern defines an abstraction allowing to associate with a part of the page an object which will drive it. Among other things, this allows CSS selectors not to be repeated and makes the tests much more readable by exposing 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 for the Google search 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 associated with CSS selectors, while the second part, ' command ', allows you to create the business methods used later in our tests. You'll notice the use of aliases (e.g., '@searchInput') in the methods to avoid repeating selectors and thus reduce the risk of errors.
Tips: For my part, I prefer to return 'this.api' in the commands, because 'this.api' corresponds to the 'browser' parameter in the tests. And so by returning it we can chain the calls of different page objects.
The nw/tests/researchOnGoogle.test.js test now looks like this:
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();
}
};
That's clearer, isn't it? Thanks to our google.js file in the page object folder, we have access to a Google method that allows us to use the page object of the same name and perform operations on the page in a more readable way than before (see: previous version ).
Command
In this part, I will explain how to build more complex commands using the prototype. This command format makes it possible to manage, among other things, asynchronisms. If your need is relatively simple and synchronous, stay on the simple format visible on the doc.
Like a page object, to create a command you must create a file in the directory provided for this purpose. Let's create the 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;
A command is therefore in Nightwatch a prototype inheriting from EventEmitter node and which contains a method 'command'.
To signal to Nightwatch that this command has been completed, we must emit the event 'complete', and for it to be chainable we return 'this'. Once integrated into the test, we end up with 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
With this article, coupled with previous on the same subject, you are now ready to write effective, readable and easy-to-maintain acceptance tests while minimizing execution 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″]
