How can I check until an element is clickable using nightwatch js? I want to click on an element but when I run nightwatch, selenium does not click on the element because it is not clickable yet.
-
What do you mean clickable? Is the element disabled? Is it hidden? – Alex R Apr 13 '16 at 02:01
-
1I'd like to see an example that would be the equivalent of `waitUntilElementIsEnabled()`. There is a helper in Selenium that combines a check for isEnabled with a check for isVisible before returning `true`. – Kyle Pittman Apr 13 '16 at 04:56
-
See [this search on Selenium's Github](https://github.com/SeleniumHQ/selenium/search?l=python&q=clickable&type=Code&utf8=%E2%9C%93) for examples – Kyle Pittman Apr 13 '16 at 04:57
4 Answers
Something like this should work. Let me know if you have questions
var util = require('util');
var events = require('events');
/*
* This custom command allows us to locate an HTML element on the page and then wait until the element is both visible
* and does not have a "disabled" state. It rechecks the element state every 500ms until either it evaluates to true or
* it reaches maxTimeInMilliseconds (which fails the test). Nightwatch uses the Node.js EventEmitter pattern to handle
* asynchronous code so this command is also an EventEmitter.
*/
function WaitUntilElementIsClickable() {
events.EventEmitter.call(this);
this.startTimeInMilliseconds = null;
}
util.inherits(WaitUntilElementIsClickable, events.EventEmitter);
WaitUntilElementIsClickable.prototype.command = function (element, timeoutInMilliseconds) {
this.startTimeInMilliseconds = new Date().getTime();
var self = this;
var message;
if (typeof timeoutInMilliseconds !== 'number') {
timeoutInMilliseconds = this.api.globals.waitForConditionTimeout;
}
this.check(element, function (result, loadedTimeInMilliseconds) {
if (result) {
message = '@' + element + ' was clickable after ' + (loadedTimeInMilliseconds - self.startTimeInMilliseconds) + ' ms.';
} else {
message = '@' + element + ' was still not clickable after ' + timeoutInMilliseconds + ' ms.';
}
self.client.assertion(result, 'not visible or disabled', 'visible and not disabled', message, true);
self.emit('complete');
}, timeoutInMilliseconds);
return this;
};
WaitUntilElementIsClickable.prototype.check = function (element, callback, maxTimeInMilliseconds) {
var self = this;
var promises =[];
promises.push(new Promise(function(resolve) {
self.api.isVisible(element, function(result) {
resolve(result.status === 0 && result.value === true);
});
}));
promises.push(new Promise(function(resolve) {
self.api.getAttribute(element, 'disabled', function (result) {
resolve(result.status === 0 && result.value === null);
});
}));
Promise.all(promises)
.then(function(results) {
var now = new Date().getTime();
const visibleAndNotDisabled = !!results[0] && !!results[1];
if (visibleAndNotDisabled) {
callback(true, now);
} else if (now - self.startTimeInMilliseconds < maxTimeInMilliseconds) {
setTimeout(function () {
self.check(element, callback, maxTimeInMilliseconds);
}, 500);
} else {
callback(false);
}
})
.catch(function(error) {
setTimeout(function () {
self.check(element, callback, maxTimeInMilliseconds);
}, 500);
});
};
module.exports = WaitUntilElementIsClickable;
Add this code as a file to your commands folder. It should be called waitUntilElementIsClickable.js or whatever you want your command to be.
Usage is:
browser.waitUntilElementIsClickable('.some.css');
You can also use page elements:
var page = browser.page.somePage();
page.waitUntilElementIsClickable('@someElement');

- 624
- 3
- 10
-
Hey, I was trying your code, but it's giving syntax error ```waitForElementClickable.js:57 .then((results) => { ^^ SyntaxError: Unexpected token => ``` Can you please assist me? – Juhi Saxena Jun 10 '16 at 12:43
-
Ahh, the code is partially written in ES6 format, I updated so that it should work with pure ES5 – Alex R Jun 10 '16 at 12:46
-
I try to use your function but nightwatch return as waitUntilElementsIsClickable is not a function. Why is this error? – nicholas Dec 11 '17 at 01:28
You can use waitForElementVisible()
combined with the :enabled
CSS pseudo-class.
For example, the following will wait up to 10 seconds for #element
to become enabled, then click it (note that the test will fail if the element doesn't become enabled after 10 seconds):
browser
.waitForElementVisible('#element:enabled', 10000)
.click('#element');

- 3,198
- 1
- 21
- 22
-
4this should be the top answer. no need in writing a custom command when this functionality already exists – king_wayne Jan 31 '19 at 16:08
Can you show an example element,usually there should be an attribute name "disabled" if the button is not clickable, this should work.
browser.assert.attributeEquals(yourCSS, 'disabled', true)

- 618
- 4
- 15
-
I'd like to see an example that would be the equivalent of `waitUntilElementIsEnabled()`. There is a helper in Selenium that combines a check for isEnabled with a check for isVisible before returning `true`. – Kyle Pittman Apr 13 '16 at 04:56
I'm unable to comment but there are a couple of issues with the code suggested by Alex R.
First, the code will not work with Firefox as geckodriver does not return a 'status'. So this:
resolve(result.status === 0 && result.value === true)
needs to be changed to this:
resolve(result.value === true).
Second, the line:
self.client.assertion(result, 'not visible or disabled', 'visible and not disabled', message, true);
doesn't work and needs to be commented out in order to get the code to run.

- 5,957
- 2
- 12
- 29

- 51
- 6