-
Notifications
You must be signed in to change notification settings - Fork 2.3k
"Reusing" element promises leads to StaleElementReferenceError #1903
Description
I'm trying to test how a page changes in response to user input. As such, the DOM is mutating, and I believe these mutations make any element
promise from before the change suspect. Given that mutating the DOM is the Angular Way, and ExpectedConditions are supposed to help test for an element's visibility/presence, it seems like EC should be hiding this problem?
However, I'm not clear if holding on to such promises across DOM mutations is just "bad form" (and I should always re-"locate" any elements that I think may have changed/moved) or if Protractor should implicitly re-locate elements for me. (Those both seem problematic, so I think this is an EC issue?)
The Selenium page on the exception is useful/frustrating reading: http://docs.seleniumhq.org/exceptions/stale_element_reference.jsp
It really seems like the case of navigating to a different page should be separated from the case of an element being destroyed and re-created which should also be separated from the case of an element being removed from the DOM. (So maybe this is a Selenium bug?)
Here is a simplified test case that demonstrates a variant on my problem using the protractor API page. Note that the test catches exceptions from the ExpectedConditions, but I'm just doing that to count the failures (I don't think every use of ExpectedConditions should do this).
describe('demo EC bug', function() {
var inputbox = $('#searchInput');
var title = $('.api-title');
it('force a StaleElementReferenceError', function() {
browser.get('/protractor/#/api', 90 * 1000);
expect(title.getText()).toBe('Protractor API Docs');
// Get an array of element promises, one for each "<li>" on the page (the
// doc links on the left side).
var deferred = protractor.promise.defer();
element.all(by.tagName('li')).then(function(elements) {
var elProms = [];
elements.forEach(function(elProm) {
elProms.push(elProm);
});
deferred.fulfill(elProms);
});
// Tell controlFlow about the array generation promise
protractor.promise.controlFlow().await(deferred);
var failureCt = 0;
// Once the array is done, put some text into the search box. This
// will cause most of the "<li>" elements to disappear, then when the
// ExpectedConditions operate on the element, a
// StaleElementReferenceError will get thrown (on some elements).
deferred.promise.then(function(elements) {
console.log('ELEMENTS:', elements.length);
// This should cause all of the <li> elements on the page to
// disappear: I believe this is effectively invalidating many of the
// element promises in the "elements" array.
inputbox.sendKeys('something unique and a bit long');
elements.forEach(function(element) {
var EC = protractor.ExpectedConditions;
var mkProm = EC.visibilityOf(element);
mkProm().then(function(visible) {
console.log('Element visibility: ', visible);
}, function(err) {
// console.log('Element ERR: ', err.message);
expect(err.message).toContain('stale element reference: element is not attached');
failureCt++;
});
});
}).then(function() {
expect(failureCt).toBe(0);
});
});
});