greggmi
greggmi

Reputation: 505

How to handle protractor promises within a for loop?

I'm trying to do some protractor testing and promises are not being resolved within the for loop.

In my test case I want to find a particular node in a ng-repeat element.

Here is the code to find such a node:

var firstProfileNode = function(nodename, profile){ 
    element.all(by.repeater('node in tree_nodes')).then(function(treeNodes){
        for(var i=0; i<treeNodes.length; i++){
            var node = treeNodes[i].element(by.css('.tree-dnd-handle'));
            node.getText().then(function(text){

                console.log(i+" : "+text);
                if (profile){
                    var pattern = '^' +nodename+' \\(\\d+ devices\\)$';
                    var regx = new RegExp(pattern);
                    if(regx.test(text)){
                        console.log('found')
                        return node;
                    }
                }else{
                    if(text === nodename){
                        return node;
                    }
                }
                });
        }
    });
  };
var test = firstProfileNode('ISR', true);

Here is the console output:

23 : edison (4 devices)
23 : ed21-mbr2
23 : ed22-mbr1
23 : ed22-mbr2
23 : ed21-mbr1
23 : c2800-12
23 : L1 (4 devices)
23 : c887VAM-1
23 : c891-1
23 : c887-1
23 : c3850-1
23 : ISR (3 devices)
found
23 : 3700 (2 devices)
23 : c3745-2
23 : c3745-1
23 : c2921-1
23 : c2800-11
23 : N7K (3 devices)
23 : n7k-2
23 : n7k-1
23 : n7k-3
23 : c2800-13
23 : c2800-14

The problem is that the getText() promise resolves after the for loop is done. Evidence of this is seen by the logged 'i' value which is 23, the final count. I'm looking for a way to have the for loop wait for the promise or another way to find the node altogether.

Upvotes: 4

Views: 1055

Answers (3)

kaushlendras
kaushlendras

Reputation: 220

You can try something like this:

var firstProfileNode = function(nodename, profile){ 
    element.all(by.repeater('node in tree_nodes')).then(function(treeNodes){
        for(var i=0; i<treeNodes.length; i++){
            var node = treeNodes[i].element(by.css('.tree-dnd-handle'));
            getNodeText(i, node);
        }
    });
  };

var getNodeText = function(i, node) {
node.getText().then(function(text){

                console.log(i+" : "+text);
                if (profile){
                    var pattern = '^' +nodename+' \\(\\d+ devices\\)$';
                    var regx = new RegExp(pattern);
                    if(regx.test(text)){
                        console.log('found')
                        return node;
                    }
                }else{
                    if(text === nodename){
                        return node;
                    }
                }
                });}
var test = firstProfileNode('ISR', true);

Upvotes: 0

alecxe
alecxe

Reputation: 473833

You are expecting the code to be executed from top to bottom synchronously, but it is actually asynchronous - the loop would be over at the moment first getText() is resolved.

I think what you need is a filter():

var firstProfileNode = function(nodename, profile) { 
    return element.all(by.repeater('node in tree_nodes')).filter(function(treeNode) {
        return treeNode.element(by.css('.tree-dnd-handle')).getText().then(function(text) {
            if (profile) {
                var pattern = '^' +nodename+' \\(\\d+ devices\\)$';
                var regx = new RegExp(pattern);
                return regx.test(text);
            } else {
                return text === nodename;
            }
        });
    }).first();
};

The firstProfileNode() function would return an ElementFinder instance corresponding to the desired filtered node element.

Upvotes: 2

Ajk_P
Ajk_P

Reputation: 1874

This is a pretty common occurrence.

The promises resolve asynch AFTER the whole for loop has been run and therefore only the last node is being returned (and i is always 23)

A common approach is to enclose the callback (i.e.)

function fooBar(node, i) {
        node.getText().then(function(text){
            console.log(i+" : "+text);
            if (profile){
                var pattern = '^' +nodename+' \\(\\d+ devices\\)$';
                var regx = new RegExp(pattern);
                if(regx.test(text)){
                    console.log('found')
                    return node;
                }
            }else{
                if(text === nodename){
                    return node;
                }
            }
        });    
}

In a separate method, so that every node and i variable is closed in scope in that method and doesn't change. Then simply call the method.

Upvotes: 0

Related Questions