Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

"Reusing" element promises leads to StaleElementReferenceError #1903

@tullmann

Description

@tullmann

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);
    });
  });
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions