2

I need some help with a nested for loop in protractor and converting/understanding promises correctly. In ‘test’ below the functionality works with all values, but as soon as I try to put in the nested for loops things go south. Any chance someone has a clean suggestion on this? I have tried the forEach which some indicate handle the promise issue inherently, but I seem to get the same results.

My Test data looks like:

objectPage.chartValues = {
[['chart','ChartID01'],['title','TitleText01'],['Name01','value01'],['Name02','Value02']],
[[‘chart','ChartID02'],['title','TitleText02'],['Name01','value01'],['Name02','Value02']],
[[‘chart','ChartID03'],['title','TitleText03'],['Name01','value01'], [‘Name02’,'Value02'],['Name03','Value03']]
}

it ('test', function (){

    for (chartNumber = 0; chartNumber < objectPage.chartValues.length; chartNumber++) {
        for (chartEntry = 1; chartEntry < ObjectPage.chartValues[chartNumber].length; chartEntry++) {

    //for readability of next call pulled out here
            chart = objectPage.chartValues[chartNumber][0][1];
            name = objectPage.chartValues[chartNumber][chartEntry][0];
            value = objectPage.chartValues[chartNumber][chartEntry][1];

            pageObject.getbackgroundcolor(chart, name).then(function (color) {
                expect(pageObject.getElementFromTable(chart, name, color).getText())
                    .toEqual(value);

            });
        }
    }
});

//function calls in pageobject  the call for get background is straight forward.

   this.getbackgroundcolor = function (chartName, valueName) {
             return element(by.id(chartName)).element(by.cssContainingText('.dxc-item', valueName)).element(by.tagName('rect')).getAttribute('fill');

//get element is very similar.
    this.getElementFromTable = function(chartName, valueName, colorname) {
    some searching stuff.. 
    return element(by.css(‘tspan'));

My results seem to indicate the look executes, but not returning from the actual expect. Finally trying to find a value for an item with background color of null. I know this is not true as I have run all values individually and in sequence without issue. Hopefully I avoided cut and past/generalization errors.

Thank you.

Update: it('Verify Charts on page ', function () {

   myChartlength = objectPage.chartValues.length;

    for (chartNumber = 0; chartNumber < myChartlength; chartNumber++) {
        (function (chartNumber) {

            myEntrylength = objectPage.chartValues[chartNumber].length;           
            chartValues = objectPage.chartValues[chartNumber];

            for (chartEntry = 2; chartEntry < myEntrylength; chartEntry++) {
                (function (chartEntry) {

                    //pulled these out for readablility of next call.
                    chart = chartValues[0][1];
                    name = chartValues[chartEntry][0];
                    value = chartValues[chartEntry][1];
                    console.log('chart: ' + chart + ', name: ' + name + ', value: ' + value);

                  page.getbackgroundcolor(chart, name).then(function (color) {
                        expect(objectPage.getElementFromTable(chart, name, color).getText()).toEqual(value);
                    });
                })(chartEntry);
            };
        })(chartNumber);
    };

});

  • you should probably re-format your question - the code should be formatted as code. use the 'code' button :] – FuzzyAmi Jul 19 '15 at 19:11
  • 1
    Sounds like a duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) to me. If not, please fix that first. – Bergi Jul 19 '15 at 19:39

2 Answers2

4

Yeah, if I'm understanding your question correctly, your problem is async. It's firing through the loops before any promises are returned.

To loop tests, the best solution I've found is to use an IIFE (Instantly Invoked Function Expression). In which, you create your loop, create the iife, and pass in the index.

Here's a basic example:

describe('to loop tests', function() {
    var data = ['1', '2', '3'];

    for(var i = 0; i < data.length; i++) {
        // create your iife
        (function(i) { 

            it('pass in the index to an iife', function() {
                console.log('i is: ' + i);
                expect(data[i]).toBe(true); 
            });

        })(i); // pass in index
    }
});

This works great for data driving tests from say a data file, or whatever. And if you need multiple loops, like in your example code, you'll just make multiple iifes.

Brine
  • 3,733
  • 1
  • 21
  • 38
1

You shouldn't use for loops with protractor or you will have a bad time. Due to the asynchronous nature of Protractor, if you need loops, I see async's map https://github.com/caolan/async as one good and clean solution.

Other option is to use ES5's map when you need loops in Protractor, such as:

[1,3,5,7].map(function(index,key){
   expect(element.all(by.css('.pages-list')).get(index).isDisplayed()).toBeFalsy()
})

In your case, I see that you need a for loops to produce array, that you later can map over it.

You can have this array with function, that uses for loops inside and returns the needed array to a callback. Simple example with one for loop

function returnIndexes(callback){ 
var exitArray = []; 
for (var i = 0; i < someArray.length; i++) {
     if(someArray[i].length > 12){
        exitArray.push(someArray[i]); 
     }

     if(i==someArray.length-1){
     callback(exitArray);
     }
}
Dejan Toteff
  • 2,075
  • 3
  • 21
  • 30