I'm trying to implement an async custom command. The image resizing example in the docs accepts a callback as its last argument. Should the callback function be supplied explicitly by the test author or is it automatically passed in by Nightwatch? Empirically it seems to be the former, but that seems cumbersome so I wanted to make sure I'm not missing something.
It's the former.
On Mon, Feb 2, 2015 at 11:33 PM, harrykao [email protected] wrote:
I'm trying to implement an async custom command. The image resizing
example in the docs accepts a callback as its last argument. Should the
callback function be supplied explicitly by the test author or is it
automatically passed in by Nightwatch? Empirically it seems to be the
former, but that seems cumbersome so I wanted to make sure I'm not missing
something.—
Reply to this email directly or view it on GitHub
https://github.com/beatfactor/nightwatch/issues/350.
In that case, async custom commands are unlike the built-in ones in the sense that you need to continue the test inside the callback. And the more custom commands you use, the more your code indents:
browser
.url('http://foo.com/')
.customCommand1(function() {
browser
.assert.visible('#something')
.customCommand2(function() {
browser
.assert.visible('#somethingElse')
.end();
});
});
Would it make sense for custom commands to receive a callback supplied by the framework (as custom assertions do)? I'd love to be able to do this:
browser
.url('http://foo.com/')
.customCommand1()
.assert.visible('#something')
.customCommand2()
.assert.visible('#somethingElse')
.end();
I think I'm starting to understand a bit better after reading the docs and the code again. Should I be emitting the complete event when the async command is done? The docs say that custom commands automatically inherit from EventEmitter if they're defined like the example, but that didn't work for me. Manual inheritance using the constructor style did though.
Also, what's the difference between this:
browser
.url('http://foo.com/')
.waitForElementVisible('body', 1000)
.assert.title('Title');
And this:
browser
.url('http://foo.com/')
.waitForElementVisible('body', 1000, function() {
this.assert.title('Title');
});
I'm not sure about this command thing either, seems like calling this.execute doesn't hit the first function.. Since it's not async, should I need to pass a callback? I followed the guide.
// commands/clickMap.js
'use strict';
exports.command = function (latlng, callback) {
var self = this;
this.execute(function () {
var ll = new google.maps.LatLng(latlng);
google.maps.event.trigger(gmap.map, 'click', { latLng: ll });
return true;
}, [], function (resp) {
if (typeof callback === 'function') {
callback.call(self, resp);
}
});
return this;
};
Usage:
'infobox condition click': function (browser) {
browser
.waitForElementVisible('#map > .gm-style', 8000)
.clickMap({ lat: 29.60689427653152, lng: -94.23797607421875 })
.execute(function () {
return window.innerWidth <= 640 || window.innerHeight <= 480;
},[], function (resp) {
var mobile = resp.value;
console.log('mobile', mobile);
if (mobile) {
browser.waitForElementVisible('.info-window', 30000)
.pause(1000)
.assert.containsText('.info-window', 'Closures on Beach Road')
.click('.close')
.waitForElementNotVisible('.info-window', 3000);
} else {
browser.waitForElementPresent('.infoBox', 30000)
.pause(1000)
.assert.containsText('.infoBox', 'Closures on Beach Road')
.click('.infoBox img')
.waitForElementNotPresent('.infoBox', 3000);
}
});
},
@beatfactor should I be inheriting from EventEmitter like in https://github.com/beatfactor/nightwatch/blob/master/lib/api/commands/pause.js
Was this ever answered?
Not for me, at least. I wasn't able to find a nice way to implement an async custom command using the pattern in the Nightwatch guide. Instead, I inherited the command from EventEmitter and called this.emit('complete'); to signal completion.
You can see an example of this in nightwatch/lib/api/commands/pause.js.
+1
exports.command = function(speed, duration, callback) {
var self = this;
console.log('executeAsync:', this.executeAsync);
this.executeAsync(
function(duration, done) { // execute application specific code
console.log('executeAsync :: first', duration, done);
var onComplete = function() {
console.warn('executeAsync :: called later');
done(true);
};
window.setTimeout(onComplete, duration);
},
[duration], // arguments array to be passed
function() {
console.log('this is called instead of anything inside executeAsync');
if (callback) {
callback.call(self);
}
}
);
return this; // allows the command to be chained.
};
this doesn't work for me...
nothing is ever called in the first function passed to executeAsync, and the callback is fired immediately.
Same here. Doesn't work for me.
any luck on this? @beatfactor it'd be great to have an example of creating a user, for example, and then continue the test. I have it in a before block which I dislike. See:
login.spec.js
var email;
var password;
var address;
module.exports = {
'@tags': ['auth', 'login'],
before: function(client, done) {
console.log('here');
client.createUser(function(result) {
email = result.email;
address = result.email.split('@')[0];
password = result.password;
done();
});
},
'can login': function(client) {
var login = client.page.login();
login.navigate().waitForElementVisible('body', 5000);
login.setValue('@emailInput', email);
login.setValue('@passwordInput', password);
login.click('@submit');
client.waitForElementVisible('#user-dropdown', 5000); // wait for api to finish and user signed in!
client.expect.element('#user-dropdown').text.to.contain(address);
client.end();
}
}
and createUser.js
var util = require('util');
var events = require('events');
var superagent = require('superagent');
var url = 'http://localhost:' + process.env.PORT + '/users.json';
var CreateUser = function() {
events.EventEmitter.call(this);
};
util.inherits(CreateUser, events.EventEmitter);
CreateUser.prototype.command = function(cb) {
var self = this;
var email = Math.random().toString(36).substring(7) + '@email.com';
var password = Math.random().toString(36).substring(7);
superagent
.post(url)
.send({
email: email,
password: password,
password_confirmation: password
})
.set('Accept', 'application/json')
.end(function(err, res){
if (err || !res.ok) {
alert('Oh no! error');
}
if (cb) {
cb.call(self.client.api, Object.assign({
email: email,
password: password
}, res.body));
}
self.emit('complete');
});
// return this;
};
module.exports = CreateUser;
without the done this technically will not wait. How can I have the createUser() command right before the setValue command?
Just wanted to send a heads up that anyone who thinks they're running into this issue should check on the execution of their callback function using something other than console.log (i.e. try actually changing something on the page instead).
Most other functions in your nightwatch test are running directly in node, so any console.log calls in those functions will show up in stdout on the terminal. On the other hand, async custom commands are running _in the selenium instance_ so any console.log calls in those functions will show up in the JS dev console of the selenium browser.
You very likely won't see this output during a test, which makes console.log a terribly misleading way to check if your async callback has been executed.
Thanks for the tip.
Is there anyway to avoid the nested code as described by @mmahalwy ?
Most helpful comment
In that case, async custom commands are unlike the built-in ones in the sense that you need to continue the test inside the callback. And the more custom commands you use, the more your code indents:
Would it make sense for custom commands to receive a callback supplied by the framework (as custom assertions do)? I'd love to be able to do this: