2

The following code gets stuck:

var Promise = require('promise');
var testPromise = function(){
  return new Promise(function(fulfill, reject){

    element.all(by.repeater('item in menu.items')).first().then(function(el){
        console.log('test f');
        fulfill(el);
        console.log('test fe');
    });

  });
};

... called by the following:

testPromise().then(function(el){
    console.log('test successful '+el);
});

The console prints

test f
test fe

And get stuck no more code is executed. It never reaches the then although fulfill has been called.

if using nested promises is an anti pattern then how do I do the following without a nested promise:

var getMenuItemEl = function(itemName){
  return new Promise(function(fulfill, reject){

    var elFound;
    element.all(by.repeater('item in menu.items')).then(function(els){
        async.each(els, function(el, callback){
            el.getText().then(function(text){
                console.log('getMenuItemEl:'+text);
                if(text === itemName){
                    elFound = el;
                }
                callback(); 
            });
        }, function(err){
            console.log('complete '+elFound);
            if(elFound){
                console.log('fulfill');
                fulfill(elFound);
                console.log('after fulfill');
            }else{
                reject('no item found');
            }
        });

    });

  });
};

This also gets stuck after the fulfill has been called

Rory G
  • 173
  • 1
  • 11

2 Answers2

2

if using nested promises is an anti pattern then how do I do the following without a nested promise

You would not use the async library. Since all your methods (element.all(), el.getText() etc) do already return promises, you don't need to go back to node-style error callbacks. From my "rules for promises", every async function (even if it is a callback) should return a promise, and use your libraries' methods to compose them. You really don't need to call the Promise constructor on your own. That iteration you are doing there can be easily done by a map over the els, then collecting all the single promises together with Promise.all.

function getMenuItemEl(itemName) {
    return element.all(by.repeater('item in menu.items'))
    .then(function(els){
        return Promise.all(els.map(function(el) {
            return el.getText()
            .then(function(text){
                console.log('getMenuItemEl:'+text);
                return (text === itemName) ? el : null;
            });
        }));
    })
    .then(function(foundEls) {
        for (var i=0; i<foundEls.length; i++)
            if (foundEls[i] != null) {
                console.log('fulfill');
                return foundEls[i];
            }
        throw new Error('no item found');
    });
}
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks for your help. Doing this synchronously would be so simple. – Rory G Oct 10 '14 at 10:51
  • The error is still there when i run this new code. It gets stuck after the last getMenuItemEl, it never reaches fulfill and no error is thrown – Rory G Oct 10 '14 at 15:45
  • I have no idea. Maybe there's a bug in the API that you use, and it returns ever-pending promises? Although in your original script, where you call `fulfill` and then nothing happens, it rather looks like there is endless loop or so after the execution of that last callback. Can you maybe set up a minimal demo that reproduces the issue so that we can investigate? – Bergi Oct 11 '14 at 15:41
  • I also dont understand how this is working. What hapens when you return foundEl[i], is this function returning a promise or an element? – Rory G Oct 13 '14 at 08:27
  • The callback function from which I `return foundEl[i]` does return an element. The `.then(…)` to which this callback is passed returns a promise for this element, as it waits for `foundEls` before executing the callback. – Bergi Oct 13 '14 at 10:07
0

The following code solves my problem, mainly due to my lack of knowledge of the protractor api

var doClickMenuItemEl = function(itemName){
  return element.all(by.repeater('item in menu.items')).filter(function(elem, index) {
    return elem.getText().then(function(text) {
        return text === itemName;
    });
  }).then(function(els){
    els[0].click();
  });   
};

It also says on https://github.com/angular/protractor/issues/379 that

Yes, this is inherited from webdriver promises, which are not quite promise A+ compliant, unfortunately.

Rory G
  • 173
  • 1
  • 11