Reputation: 505
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
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
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
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