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:
User then clicks the edit button and the call out becomes this:
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!