Reputation: 8195
Using selenium-webdriver (api docs here), how can you wait for an element to be visible?
I have the following functions, part of a home-made set of testing helpers, and the first one works but the second one fails (eg. it times out wating for and element to be visible even if it exists - as confirmed by the first function that works - and is visible - as confirmed by all imaginable tests and inspections of the page html/css/js).
Here they are:
/**
* Wait for an element to exist
*
* @param {object} locator
* @param {int} timeout (ms)
*
* @return {Promise<element>}
*/
// !! THIS WORKS OK
exports.waitForElement = function (locator, timeout) {
var waitTimeout = timeout || DEFAULT_TIMEOUT;
return this.wait(until.elementLocated(locator), waitTimeout)
.then(() => {
return this.findElement(locator);
});
};
/**
* Wait for an element to exist and then wait for it to be visible
*
* IMPORTANT: this is probable what you want to use instead of
* waitForVisibleElement most of the time.
*
* @param {hash} locator
* @param {number} timeout
*
* @return {Promise<element>}
*/
// !! THIS FAILS TO WORK AS EXPECTED
exports.waitForVisibleElement = function (locator, timeout) {
var waitTimeout = timeout || DEFAULT_TIMEOUT;
return this.waitForElement(locator, waitTimeout)
.then(el => {
console.log('--- element found:', el);
return this.wait(until.elementIsVisible(el), waitTimeout)
.then(() => {
console.log('--- element visible!');
// this is to make sure we are returning the same kind of
// promise as waitForElement
return this.findElement(locator);
});
});
};
...I tested in multiple contexts, so it's no other cause of the problem then the code inside waitForVisibleElement
but I can't seem to find any reason for why it does not work!
As clarification, this
for that code ends up being the webdriver instance (the result of new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build()
) after an augment
method monkeypatches a given webdriver object... probably a questionable design pattern, but no the cause for my problem here :)
UPDATE: Apparently this only happens for XPath locators, like { xpath: '//*[contains(text(), "first name")]' }
... not that it makes any more sense now. Also, it's the same for Firefox, so it's not a weird chrome-webdriver thingy...
Upvotes: 4
Views: 18225
Reputation: 42518
It most likely is a Promise issue. Try this instead:
exports.waitForElement = function (locator, timeout) {
var timeout = timeout || DEFAULT_TIMEOUT;
return this.wait(until.elementLocated(locator), timeout);
};
exports.waitForVisibleElement = function (locator, timeout) {
var timeout = timeout || DEFAULT_TIMEOUT;
var element = this.wait(until.elementLocated(locator), timeout);
return this.wait(new until.WebElementCondition('for element to be visible ' + locator, function() {
return element.isDisplayed().then(v => v ? element : null);
}), timeout);
};
Usage:
driver.get("...");
driver.waitForElement(By.id("..."), 2000).getText().then(function(text){
console.log(text);
});
driver.waitForVisibleElement(By.id("..."), 2000).getText().then(function(text){
console.log(text);
});
Upvotes: 3