Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

E2E tests with multiple pages with information from browser

I am writing an E2E test with protractor. I had to fetch information from the browser and execute a step multiple times.

I am testing one screen which will start when a

  • User clicks 'Start'
  • lands on a new page
  • The workflow below is invoked with count being passed as argument
  • id the html id does not change. the value changes when queried again after submitting the current form.
for(i = 0 ; i < count ; i++){
   console.log("counter is "+i);
   element(by('id')).evaluate('value').then(function(v) {
    // do some action on UI based on v
    element(by('id1')).sendKeys(v+v);
    // submit etc., 
    // some angular code runs in the frontend.
   }
   // need to wait since webdriver jumps to the next one without this completing
}

Many blog posts/documentations suggests you cannot use it in a loop, but does not suggest any alternative way to do this.

Any suggestions appreciated.

Never use protractor element statements inside loop: The simple reason is that the webdriverJS (protractor) API is asynchronous. Element statements returns a promise and that promise is in unresolved state while the code below the statements continues to execute. This leads to unpredictable results. Hence, it is advisable to use recursive functions instead of loops.

source: http://engineering.wingify.com/posts/angularapp-e2e-testing-with-protractor/

Edit: updated question with details of workflow.

like image 829
Sairam Avatar asked Feb 07 '17 04:02

Sairam


People also ask

How do you do end to end testing for Web applications?

Key Steps in Setting up End-to-End Tests:Review the requirements you'll be using end-to-end testing to validate. Set up the test environments and outline the hardware /software requirements. Define all the processes of your systems and its integrated subsystems. Describe the roles and responsibilities for each system.

Can E2E testing be automated?

Automated end-to-end testingEnd-to-end tests can help automate user-interaction testing, saving valuable time. Once you've decided on the test cases, you can write them as code and integrate them with an automated testing tool. For example, you can use a CI/CD pipeline to automate the end-to-end testing of software.

What is end2end testing?

End-to-end testing is a methodology that assesses the working order of a complex product in a start-to-finish process. End-to-end testing verifies that all components of a system are able to run and perform optimally under real-world scenarios.


2 Answers

Looping in protractor works like this

describe('Describe something', function() {
    var testParams = [1,2,3,4,5,6,7,8,9,10];
    beforeEach( function() {
        // ...
   });

for (var i = 0; i < testParams.length; i++) {
  (function (testSpec) {
    it('should do something', function() {
        // inside loop
    });

  })(testParams[i]);

};
});

Edit : I might be mis-understanding your question, but it seems to me you want to complete all(dynamic count) actions on the page, before going to the next one ?

it('should clear old inspections', function() {  
                  inspectieModuleInspectieFixture.getRemoveInspectionButton().count().then(function (value) {
                        if(value == 0){
                            console.log('--- no inspections to remove ---');
                        }
                        for(var i = 0; i < value; i++){
                            //global.waitForClickable(inspectieModuleInspectieFixture.getRemoveInspectionButtonList(i+1));
                            inspectieModuleInspectieFixture.getRemoveInspectionButtonList(i+1).click();
                            console.log('iteration '+i + 'count '+value )
                        };
                    });
                    global.wait(5000);

            }); */

this counts elements on the page and then it performs an action for the ammount of elements it found

In the above example I use containers to hold my elements, so my code remains readable (i.e. inspectieModuleInspectieFixture.getRemoveInspectionButton() holds $(".elementSelectorExample")

There is also a 'global.waitForClickable' commented, that is reffering to a 'time module' I've created that extends the functionality of 'wait', in this case it waits till the element is vissible/clickable.

This is easily mirrored perhaps something like this :

    waitForElementNoDisplay: function(element){
    return browser.wait(function() {
        return element.isDisplayed().then(function(present) {
            return !present;
        })
    });
},

this will make protractor WAIT untill an element is no longer displayed.(Display:none)

like image 32
Rheijn Avatar answered Sep 26 '22 03:09

Rheijn


It is usually not recommended to use a loop when an iteration has an asynchronous call.

The reason is that the first asynchronous calls is executed after the last iteration of the loop when i is already equal to count. Thus, it makes it difficult to break the loop and to keep track of the value of i.

On way to tackle the issue is to use a recursive function :

var count = 3;
var results = [];

function iterate(i, n) {
  if(i < n) {
    console.log(`counter is ${i}`);

    browser.refresh();
    return element(by.id('h-top-questions')).getText().then(function(text) {
      results.push(`${i}:${text}`);
      return iterate(i + 1, n);
    });
  }
}

iterate(0, count).then(function(){
  console.log("done!", results);
});

But a better way would be to iterate with promise.map on an array sized to the number of iterations:

var count = 3;

protractor.promise.map(Array(count).fill(0), function(v, i) {
  console.log(`counter is ${i}`);

  browser.refresh();
  return element(by.id('h-top-questions')).getText().then(function(text) {
    return `${i}:${text}`;
  });
}).then(function(results){
  console.log("done!", results);
});

You could also keep using a loop. First you'll have to use the let statement to get the value of i in an asynchronous function (ES6). Then call all the synchronous code with browser.call to synchronize the execution:

var count = 3;
var results = [];

for(let i = 0 ; i < count ; i++){
  browser.call(function(){
    console.log(`counter is ${i}`);

    browser.refresh();
    element(by.id('h-top-questions')).getText().then(function(text) {
      results.push(`${i}:${text}`);
    });
  });
}

browser.call(function() {
  console.log("done!", results);
});
like image 60
Florent B. Avatar answered Sep 27 '22 03:09

Florent B.