Nix
Nix

Reputation: 58522

Protractor Find and Click Element

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

Answers (2)

mvndaai
mvndaai

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

Nix
Nix

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

Related Questions