13

I have a set of smokescreen tests that are all pretty much identical. I would like to put them into a loop and loop at an array of parameters. However, the tests are running asynchronously and so the loop completes before the tests are run. This results in the test being run 8 times on the 8th parameter instead of once for each parameter.

describe('Admin Console Campaigns', function() {
    var ptor;
    var adminUrl;
    var testParams = [
        {title: 'Dashboard', urlSuffix: '/communic8' },
        {title: 'Campaign Report', urlSuffix: '/Reports/Campaign' },
        {title: 'Partner Campaign Usage', urlSuffix: '/Reporting/PartnerCampaignUsage' },
        {title: 'Campaign Template Usage', urlSuffix: '/Reporting/CampaignTemplateUsage' },
        {title: 'Email Usage Report', urlSuffix: '/Reports/EmailUsage' },
        {title: 'Campaign Templates', urlSuffix: '/CampaignTemplates' },
        {title: 'Campaign Template Groups', urlSuffix: '/CampaignTemplateGroups' },
        {title: 'New Template', urlSuffix: '/CampaignTemplates/Add' }
    ];

    beforeEach(function() {
        ptor = protractor.getInstance();
        ptor.ignoreSynchronization = true;
        var testParams = smokescreenTestConfig.adminCampaigns;
        adminUrl = ptor.params.http + ptor.params.client + ptor.params.staging + ptor.params.sharedvue + ptor.params.admin;
    });

    afterEach(function(){

    });

    for(var i=0; i < testParams.length; i++){
        var testParam = testParams[i];

        it('should have a ' + testParam.title + ' tab', function() {
            testUrl = adminUrl + testParam.urlSuffix;
            basicTestFunctions.pageExists(testUrl, ptor, browser, testParam.title);
        }, 60000);
    };
});

Does anyone have an idea of how to force the loop to wait on the tests?

Robert McCraw
  • 663
  • 3
  • 8
  • 22

3 Answers3

24

Ok figured this one out a while ago, sorry I forgot I had posted here. We basically created a configuration array in another file, (though this is not necessary, just makes the code easier to read) and then pulled that array into a var right above the test we wanted to repeat. Then we surrounded the test in a function inside of a loop and passed the lines from our array in with each loop.

var testParams = testConfig.testArray;

for (var i = 0; i < testParams.length; i++) {

  (function (testSpec) {
    it('write your test here', function () {
      //test code here
    });
  })(testParams[i]);

};

The key here is that "testParams[i]" at the end of the function passing in the iteration of the loop. This forces synchronous execution.

If you want to get really crazy we also ended up writing a batch file that runs this smokescreen about 50 times consecutively across all of our clients. We smokescreen our entire platform in about 10 minutes.

Community
  • 1
  • 1
Robert McCraw
  • 663
  • 3
  • 8
  • 22
  • 1
    Thank you for coming back and updating this. This solution worked for me! – Drew May 19 '14 at 19:57
  • Say that loop has more `it()` blocks. Is there a way I can `continue;` to the next iteration (skipping other `it()` blocks) based on a condition within the first `it()` block? I'm not able to call continue - `SyntaxError: Illegal continue statement` – luker02 Dec 08 '15 at 20:54
  • Does Chutzpah or Karma recognise these test case?I am able to make it work in SpecRunner.html but chutzpah/karma doesnt recognise..Any comment? – sajesh Nambiar Sep 15 '17 at 16:13
10

You can also create an array with specs:

var testParams = testConfig.testArray;

testParams.forEach(function(testSpec) {
    it('write your test here', function() {
        //test code here
    });
});

This should work like the solution proposed by Robert McCraw.

Mani Gandham
  • 7,688
  • 1
  • 51
  • 60
michelesr
  • 168
  • 2
  • 8
  • Why `.map()` and no `.forEach()`? – Daniel Kucal Jan 16 '18 at 09:39
  • @DanielKucal there's no difference in the result, although maybe using `forEach()` is more appropriate for that case, because you just want to loop and you don't expect to build an array of results. – michelesr Jan 17 '18 at 11:28
2

With some asynchronous code, you can solve this quite easily.

instead of running it('description', function () { // my test }); use it('description', function (done) { // my test with a call to done() at the end }

You can use this snippet as an example :

for (var i = 0; i < 10; i++) {
  it('should work for ' + i, function (done) {
    setTimeout(done, 1000);
  });
}  

The expected output :

✓ should work for 0 (1000ms)
✓ should work for 1 (1001ms)
✓ should work for 2 (1001ms)
✓ should work for 3 (1001ms)
✓ should work for 4 (1001ms)
✓ should work for 5 (1001ms)
✓ should work for 6 (1002ms)
✓ should work for 7 (1001ms)
✓ should work for 8 (1001ms)
✓ should work for 9 (1002ms)

Hope this helps.

Furzel
  • 606
  • 8
  • 18
  • This solution throws the error "Do not use a done callback with WebDriverJS tests". Any ideas? – Drew May 12 '14 at 18:01
  • I'm afraid not, could you post some code ? ( Ideally on another question ) – Furzel May 12 '14 at 18:06
  • In an angular protractor test, if I paste in your exact `for...` example with the done callback and setTimeout, the test errors out and the console displays ` /node_modules/protractor/jasminewd/index.js:44 throw new Error('Do not use a done callback with WebDriverJS tests ^ Error: Do not use a done callback with WebDriverJS tests. The tests are patched to be asynchronous and will terminate when the webdriver control flow is empty. ` – Drew May 12 '14 at 18:26
  • OK, I'll try with jasmine ( probably runed this snippet with mocha ) and report back if I find a way. – Furzel May 13 '14 at 09:19
  • In case you're not checking this we figured it out above. – Robert McCraw May 20 '14 at 20:00