Search code examples
angularjsprotractormeanjs

Intermittent "element is not found" in protractor


In Protractor, I am trying to write a function to simulate clicking a responsive navigation menu item (i.e. menu is either a bar of dropdowns across the top or a hamburger link if mobile). The structure of the bar is defined by MEANJS with a few id="" attributes mixed in.

It seems to work exactly as I want when I run it one spec at a time, like so:

protractor app/tests/e2e/conf.js --suite mySuite

but when I run with the full test (that only has 4 tests in it), like so:

protractor app/tests/e2e/conf.js

I start to get this error intermittently (source of error is in 2 places below):

Failed: element not visible

Here's my function

commonPOs.clickNavBar = function(mainTab, linkUrl) {
  var deferred = protractor.promise.defer();

  var hamburger = element(by.id('nav-hamburger'));
  var linkCssExpression = 'a[href*="' + linkUrl + '"]';

  hamburger.isDisplayed().then(function(result) {
    if ( result ) {
      hamburger.click().then(function() {
        var navBar = hamburger
          .element(by.xpath('../..'))
          .element(by.id('myapp-navbar'));
          return clickItNow(mainTab, linkUrl, navBar);
      });
    } else {
      return clickItNow(mainTab, linkUrl, element(by.id('myapp-navbar')));
    }
  });

  return deferred.promise;

  function clickItNow(mainTab, linkUrl, navBar) {
    var link;
    if(mainTab) {
      // if mainTab was  passed, need to
      // click the parent first to expose the link
      var parentLink;

      if (mainTab == 'ACCTADMIN') {
        parentLink = navBar.element(by.id('account-admin-menu'));
      }
      else {
        parentLink = navBar.element(by.linkText(mainTab));
      }
      expect(parentLink.isPresent()).toBeTruthy();
      parentLink.click();   // FIRST PLACE ERROR HAPPENS
      link = parentLink.element(by.xpath('..')).element(by.css(linkCssExpression));
    } 
    else {
      link = navBar.element(by.css(linkCssExpression));
    }

    expect(link.isPresent()).toBeTruthy();
    link.click();     // SECOND PLACE ERROR HAPPENS
    return deferred.fulfill();
  }
};

I have tried to use both of these but neither work:

browser.sleep(500);
browser.driver.wait(protractor.until.elementIsVisible(parentLink));

What NOOB async error am I making?


Solution

  • Self answer... but any better answer will win the green check mark!!!

    EDITED - After more problems, found a nice 'waitReady()' contribution from elgalu.

    There were a few obvious problems with my original code, but fixing them did not solve the problem. After working through what I could, the solution seemed to be the expect(navBar.waitReady()).toBeTruthy(); lines I added. I also had to split the nested function out. Anyway, here is the new code that seems to be working now. A better (comprehensive) answer will get the green checkmark! I'm pretty sure there's a flaw or 2 here.

    commonPOs.clickNavBar = function(mainTab, linkUrl) {
      var deferred = protractor.promise.defer();
    
      var hamburger = element(by.id('nav-hamburger'));
    
      hamburger.isDisplayed().then(function(result) {
        if ( result ) {
          hamburger.click().then(function() {
            var navBar = hamburger
              .element(by.xpath('../..'))
              .element(by.id('myapp-navbar'));
              return clickItNow(mainTab, linkUrl, navBar, deferred);
          });
        } else {
          return clickItNow(mainTab, linkUrl, element(by.id('myapp-navbar')), deferred);
        }
      });
    
      return deferred.promise;
    };
    
    function clickItNow(mainTab, linkUrl, navBar, deferred) {
      var targetLink;
      var linkCssExpression = 'a[href*="' + linkUrl + '"]';
    
      expect(navBar.waitReady()).toBeTruthy();
    
      if(mainTab) {
        // if mainTab was  passed, neet to
        // click the parent first to expose the link
        var parentTabLink;
    
        if (mainTab == 'ACCTADMIN') {
          parentTabLink = navBar.element(by.id('account-admin-menu'));
        }
        else {
          parentTabLink = navBar.element(by.id('main-menu-' + mainTab));
        }
        // expect(parentTabLink.isDisplayed()).toBeTruthy();
        expect(parentTabLink.waitReady()).toBeTruthy();
        parentTabLink.click();
        targetLink = parentTabLink.element(by.xpath('..')).element(by.css(linkCssExpression));
      }
      else {
        targetLink = navBar.element(by.css(linkCssExpression));
      }
    
      expect(targetLink.isDisplayed()).toBeTruthy();
      targetLink.click().then(function() {
        return deferred.fulfill();
      });
    }