Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Protractor with loops to fill in a form getting data from a Cucumber.js table

(I have seen this SO discussion, but was not sure how to apply it to my case, so I’m asking a new question. Hope it’s not a duplicate)

I am testing a form written in Angular using Protractor with Cucumber.js.

So what I would like to do is to tell Protractor to go click on the title of a field (which is a link) then, when that field appears, enter some text in it, then move on to the title of the next field, and so on.

Here is my step in Cucumber:

When I fill the form with the following data
    | field            | content           |
    | First Name       | John              |
    | Last Name        | Doe               |
    | Address          | Some test address |
# and so forth

Here’s a half-hearted attempt at step definition:

this.When(/^I fill the form with the following data$/, function (table, callback) {
    data = table.hashes();
    # that gives me an array of objects such as this one:
    # [ { field: 'First Name', content: 'John' },...]

    for (var i = 0; i < data.length; i++){
        var el = element(by.cssContainingText('#my-form a', data[i].field));
          el.click().then(function(){
                var fieldEl = el.element(by.xpath("../.."))
                    .element(by.css('textarea'));
                fieldEl.sendKeys(data[i].content);
            });
        }
    };
    callback();
});

But of course, this isn't working, because even before Protractor has time to click on a field name and enter the necessary data into the field, the callback function is called, and Cucumber moves to the next step.

So my question is, how can I, using Protractor with Cucumber.js, write the step to insert data defined in the Cucumber table into the form fields? Is this feasible using a for loop?

like image 936
azangru Avatar asked Sep 28 '22 04:09

azangru


1 Answers

Your loop is enqueuing promises, so the loop finishes before any "clicking" or sending of keys happens. You need to invoke callback after all the promises have resolved.

I see two solutions (I think). You could keep track of the promises in an array, and then use protractor.promise.all (see http://spin.atomicobject.com/2014/12/17/asynchronous-testing-protractor-angular/) to wait for the array of promises to finish. First save the promise in a var promises = [] array:

var p = el.click().then(function(){ ... });
promises.push(p)

Then outside the loop:

protractor.promise.all(promises).then(callback);

Or, you can rely on the ControlFlow to keep your promises ordered in the loop, and call the callback in the last iteration of the loop:

var p = fieldEl.sendKeys(data[i].content);
if (i === data.length - 1) { // beware: you want to check "i" inside the loop and not in a promise created in the loop.
     p.then(callback);
}

Despite all the text to the contrary, I cannot promise that either of these work. Hopefully they at least point you in the right direction.

like image 152
P.T. Avatar answered Oct 05 '22 07:10

P.T.