Reputation: 58522
I have a pretty simple setup. I need to be able to find a link and click it. But whenever I find it and click()
it i get
StaleElementReferenceError: stale element reference: element is not attached to the page document
Can someone explain to me what I am doing wrong? If I target it via css it works fine. Its only when I used the protractor extensions that it fails. Nothing is changing on the page and I have tried doing a "waitForAngular" as well as a long timeout. Same result.
<li ng-click="getLocation(l)" class="" ng-repeat="l in location" >
<div class="location_name">{{ l.LocationID }} - {{l.LocationName}}</div>
</li>
var locations= element.all(
by.binding('{{l.LocationName}}'))
.each(function(item){
item.getText().then(function(text){
if(text == name){
console.log("found", item);
item.click()
console.log("clicked")
found = true;
}
});
});
An added plus would be to explain to me how to get the debugger working. I'm using the latest version of protractor(0.20.1)
Failed to open socket on port 5858, waiting 1000 ms before retrying
Ex Dump
StaleElementReferenceError: stale element reference: element is not attached to the page document
(Session info: chrome=33.0.1750.146)
(Driver info: chromedriver=2.9.248307,platform=Mac OS X 10.9.1 x86_64) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 5 milliseconds
For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.40.0', revision: 'fbe29a9', time: '2014-02-19 20:54:28'
System info: host: '.home', ip: '192.168.1.2', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.9.1', java.version: '1.6.0_65'
Session ID: 935f7630f50d9b1b4c8da73261e54153
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities [{platform=MAC, acceptSslCerts=true, javascriptEnabled=true, browserName=chrome, chrome={userDataDir=/var/folders/qj/xvhg6hv5245cryry73wygbkw0000gn/T/.org.chromium.Chromium.b5z7Xe}, rotatable=false, locationContextEnabled=true, version=33.0.1750.146, takesHeapSnapshot=true, cssSelectorsEnabled=true, databaseEnabled=false, handlesAlerts=true, browserConnectionEnabled=false, nativeEvents=true, webStorageEnabled=true, applicationCacheEnabled=false, takesScreenshot=true}]
Stacktrace:
Error
at null.<anonymous> (/Users//work////selenium/spec/locationsSpec.js:21:3)
at Object.<anonymous> (/Users//work////selenium/spec/locationsSpec.js:7:1)
At async task:
StaleElementReferenceError: stale element reference: element is not attached to the page document
(Session info: chrome=33.0.1750.146)
(Driver info: chromedriver=2.9.248307,platform=Mac OS X 10.9.1 x86_64) (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 5 milliseconds
For documentation on this error, please visit: http://seleniumhq.org/exceptions/stale_element_reference.html
Build info: version: '2.40.0', revision: 'fbe29a9', time: '2014-02-19 20:54:28'
System info: host: '.home', ip: '192.168.1.2', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.9.1', java.version: '1.6.0_65'
Session ID: 935f7630f50d9b1b4c8da73261e54153
Driver info: org.openqa.selenium.chrome.ChromeDriver
Capabilities [{platform=MAC, acceptSslCerts=true, javascriptEnabled=true, browserName=chrome, chrome={userDataDir=/var/folders/qj/xvhg6hv5245cryry73wygbkw0000gn/T/.org.chromium.Chromium.b5z7Xe}, rotatable=false, locationContextEnabled=true, version=33.0.1750.146, takesHeapSnapshot=true, cssSelectorsEnabled=true, databaseEnabled=false, handlesAlerts=true, browserConnectionEnabled=false, nativeEvents=true, webStorageEnabled=true, applicationCacheEnabled=false, takesScreenshot=true}]
==== async task ====
WebElement.getText()
at element.(anonymous function) [as getText] (/Users//work////selenium/node_modules/protractor/lib/protractor.js:599:32)
at /Users//work////selenium/helpers/locations.js:10:18
at /Users//work////selenium/node_modules/protractor/lib/protractor.js:367:11
at Array.forEach (native)
at /Users//work////selenium/node_modules/protractor/lib/protractor.js:366:13
==== async task ====
asynchronous test function
Upvotes: 3
Views: 7988
Reputation: 3831
The problem is the ng-repeat
. The object will replace itself whenever an update happens. I would actually recommend update they ng-repeat to ng-repeat="l in location track by $index"
so they get replaced correctly. That might not fix the issue.
Rather than using a .each
do a .filter
var locations = element.all(by.binding('{{l.LocationName}}'))
.filter(function(elem, index) {
return elem.getText().then(function(text) {return text === name;});
}).click();
http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.filter
Upvotes: 0
Reputation: 58522
I believe the underlying issue was I was navigating away while the getText
was being called. The below block of code accomplishes what I needed
LocationsPage.prototype.goTo = function(name) {
var locations = element.all(by.repeater('l in location'));
expect(locations.count()).toNotBe(0);
return locations.map(
function(locationElement, index) {
return {
index: index,
text: locationElement.getText()
};
}
).then(function(items) {
_.each(items, function(item) {
if (item.text == name) {
console.log("clicking ", name)
locations.get(item.index).click();
};
});
});
}
* Leaving the below here just in case someone wants an example of how to do a more complicated/custom promise flow*
I found a solution but its way to complicated. Hoping someone can help me simplify this..
I believe the underlying issue was I was navigating away while the getText
was being called.
LocationsPage.prototype.goTo = function(name){
var locations = element.all(by.repeater('l in location'));
expect(locations.count()).toNotBe(0);
return protractor.promise.createFlow(function(flow){
var found;
var locations = element.all(by.repeater('l in location'));
var searchPromise = protractor.promise.defer();
flow.execute(function(){
var idx = 0, found = -1;
locations.each(function(item){
var _idx = idx;
if(found >= 0){
console.log("skipping")
return;
}
item.getText().then(function(text){
if(text == name){
console.log("setting found.")
found = _idx;
}
if(found >= 0 || _idx === locations.count()-1){
searchPromise.fulfill(found);
}
});
idx++;
});
return searchPromise.promise;
})
flow.execute(function(){
var promise = protractor.promise.defer();
searchPromise.then(function(idx){
locations.get(idx).click().then(function(){
promise.fulfill(true);
});
})
return promise.promise;
});
});
}
You will notice some other stuff in there protractor.promise.createFlow
I had to use that because my expects
where not being correctly resolved when I used just protractor.promise.defer()
Upvotes: 3