1

This is going to be a long question :p
I have this test which fails about 50% of the time with the "No element found" fail even though I do explicit waits on element.isPresent and element.isDisplayed and wait for every promise to be resolved, basically removing any asynchronicity.

Basically, the scenario goes as follows: User hovers mouse over a table cell, a call out appears (and by appears I mean is created not just made visible), which looks like this:
http://puu.sh/lRCgg/e85a013297.png
User then clicks the edit button and the call out becomes this:
enter image description here
User puts in some text, clicks the save button and sees that the value in the call box has been updated. User repeats the action but this time clicks the cancel button and sees the changed value has been discarded.
What I do other than that is that I save the initial value and repeat the "hover and edit" action to get the contents of the cell back to what they were before the test was run.

Here is the code:
spec:

var editMemoBoxAction = function(box){
 var data = page.editMemoBox(box,'',false);
 return data.then(function(backup){
  if(backup == 'No data.') backup = '';
  expect(page.editMemoBox(box,'abc',true)).toEqual('abc');
  expect(page.editMemoBox(box,'xyz',false)).toEqual('abc');
  return page.editMemoBox(box,backup,true);
 });
};

it('does stuff', function(){
 page.mouseOver('Future Contract Action').then(function(){
  editMemoBoxAction('Future Contract Action').then(function(){
   page.mouseOver('Future Contract Action');
  });
 });
});

page object:

this.mouseOver = function(box){
var headers = element.all(by.xpath('//table/thead/tr/th')).map(function(elm){
  return elm.getText();
});
return headers.then(function(list){
  if(list.length == 0) return undefined;
  var elm = element(by.xpath('//table/tbody/tr[1]/td[' + (list.indexOf(box) + 1) + ']'));
  return browser.actions().mouseMove(elm).perform().then(function(){
    return true;
  });
});


};

this.editMemoBox = function(box, value, save){

var headers = element.all(by.xpath('//table/thead/tr/th')).map(function(elm){
  return elm.getText();
});
return headers.then(function(list){
  if(list.length == 0) return undefined;
  var timeout = 10000;
  var text = element(by.xpath('//div[@class=\'popover-content\']/div/div/div[1]')); // CONTAINS THE INITIAL TEXT VALUE OF THE CALL OUT
  return browser.wait(function(){return text.isPresent;}, timeout).then(function(){
    return browser.wait(function(){return text.isDisplayed;}, timeout).then(function(){
      return text.getText().then(function(backup){
        if (backup == 'No data.') backup = '';
        var button = element(by.xpath('//div[@class=\'popover-content\']/div/div/div[2]')); // THE EDIT BUTTON
        return browser.wait(function(){return button.isPresent;}, timeout).then(function(){
          return browser.wait(function(){return button.isDisplayed;}, timeout).then(function(){
            return button.click().then(function(){
              var edit = element(by.model('data.editValue')); // THE EDITABLE TEXT AREA
              return browser.wait(function(){return edit.isPresent;}, timeout).then(function(){
                return browser.wait(function(){return edit.isDisplayed;}, timeout).then(function(){
                  return edit.clear().then(function(){
                    return edit.sendKeys(value).then(function(){
                      var confirm; // EITHER THE SAVE OR CANCEL BUTTON
                      if(save == true){
                        confirm = element(by.xpath('//span[@class=\'glyphicon glyphicon-ok glyphicon-lg\']'));
                      }
                      else {
                        confirm = element(by.xpath('//span[@class=\'glyphicon glyphicon-remove glyphicon-lg\']'));
                      }
                      return browser.wait(function(){return confirm.isPresent;}, timeout).then(function(){
                        return browser.wait(function(){return confirm.isDisplayed;}, timeout).then(function(){
                          return confirm.click().then(function(){
                            var result = element(by.xpath('//div[@class=\'popover-content\']/div/div/div[1]')); // THE NEW VALUE OF THE CALL OUT
                            return browser.wait(function(){return result.isPresent;}, timeout).then(function(){
                              return browser.wait(function(){return result.isDisplayed;}, timeout).then(function(){
                                return result.getText();
                              });
                            });
                          });
                        });
                      });
                    });
                  });
                });
              });
            });
          });
        });
      });
    });
  });
});

};

This is so very ugly because I tried everything to remove any asychronicity that exists anywhere in the code because my problem is definitely a timing one and I'm on day 2 of this annoying thing.
It might be worth to copy the code in a text editor which colors it.

Right now, it shouldn't have any timing issues because before every interaction with the elements I wait explicitly for element.isPresent and element.isDisplayed and at every point a promise appears I wait for it to be resolved before doing anything else.

What it does right now is that it fails with "No element found" at one of the elements I'm interacting with (not the same one every time). About 50% of the time it goes through.

Please let me know if I can make this any clearer.

I know it's a lot of reading but it's either an interesting issue or I'm dumb, both of which should be fun :p

Thanks!

Vlad Vidac
  • 978
  • 1
  • 10
  • 26
  • Oh, my eyes, interesting to know what is the "cyclomatic complexity" and "maintainability index" of the presented code? :) – alecxe Dec 11 '15 at 15:55

1 Answers1

2

Things I would try:

Community
  • 1
  • 1
alecxe
  • 462,703
  • 120
  • 1,088
  • 1,195
  • Both Angular and CSS animations are already disabled. Implicit wait timeout is already 10 seconds. Once I identify the source of the issue refactoring will definitely be done :p – Vlad Vidac Dec 11 '15 at 16:00
  • 1
    @Vlad okay, you may increase the implicit timeout value. Updated the answer with more options. Btw, is this browser-specific, or you can reproduce it on different browsers? – alecxe Dec 11 '15 at 16:01
  • 1
    @Vlad one more option added. – alecxe Dec 11 '15 at 16:04
  • @alexcxe it fails on Firefox as well. The isElementPresent definitely did something, it actually waits and fails after timing out. It seems that the mouse randomly becomes "not over", but now I can actually see it happening. I will accept your answer because of this, at least I know where the problem is. The mouseOver action actually is like a toggle so I'm not sure how I can check if the mouse is over before calling mouseOver though. Thanks for the help so far! – Vlad Vidac Dec 11 '15 at 16:17