37

I've got many instability with Protractor, and I'm sure there is something I don't understand. Sometimes I need use the .then() when clicking on a button before continuing, sometimes it don't have any impact and I should not use .then() or the test failed.

I wonder when should I use the .then() callback when testing in Protractor ? Example :

createAccountForm = $('#form-create-account');
submitButton = createAccountForm.$('button[type=submit]');

browser.wait(EC.elementToBeClickable(submitButton), 5000);
submitButton.click(); // .then(function(){   <-- uncomment in the .then form

// find the confirmation message
var message = $('.alert-success');
browser.wait(EC.visibilityOf(message), 5000);
log.debug('After visibilityOf');

expect(message.isPresent()).to.be.eventually.true;
// }); --> uncomment when in .then form

When I use this form of test (without .then()) I see on browser that the click on the button is not done, the test continue with the following expect and then stop.

If I use the .then() form, the click on the button is done, and the test continue without error.

On other test, I don't need to use the then() callback when clicking on button.

So , when should I use the .then() and when not ?

Jean-Marc

jmcollin92
  • 2,896
  • 6
  • 27
  • 49
  • @emragins I understand emragins's answer, except 2. when you need the result of a driver command you should use .then, And i don't understand why in jmcollin92's code, the test in the form of 'without .then()' will fail. emragins's sample code pretty much include jmcollin92's code in a manner without .then, he just ADDED a MongoDB task using protractor.promise.defer(). But jmcollin92's original button click and confirmation message code without using .then() is the SAME as what emragins does in his answer. – user1559625 Feb 05 '23 at 10:41
  • @emragins your code on 'click the button and verify confirmation message' is exactly same as jmcollin92's code. jmcollin92 said 'When I use this form of test (without .then()) I see on browser that the click on the button is not done, the test continue with the following expect and then stop'. How is it possible that his code runs async and fail, but your code pass? – user1559625 Feb 05 '23 at 10:57

2 Answers2

41

The answer of this question can be found in this post : http://spin.atomicobject.com/2014/12/17/asynchronous-testing-protractor-angular/

That is :

  1. Protractor enqueue all driver commands in the ControlFlow,
  2. when you need the result of a driver command you should use .then,
  3. when you don't need the result of a driver you can avoid .then but all following instructions must be enqueued in the ControlFlow else they will be run before commands in the queue leading to unpredictable result. So, if you want to run a non driver tests command, you should add it into the .then callback or wrap the test into a Promise and enqueue the test in the ControlFlow. See example below.

Here is an example of my test working without .then :

log.debug('test0');

// enqueue the click
submitButton.click(); 
var message = $('.alert-success'); 

// enqueue the wait for message to be visible  
browser.wait(EC.visibilityOf(message), 5000);  

log.debug('test1');

// enqueue a test
expect(message.isPresent()).to.be.eventually.true;
log.debug('test2');

// a function returning a promise that does an async test (check in MongoDB Collection)
var testAccount = function () {           
    var deferred = protractor.promise.defer();

    // Verify that an account has been created
    accountColl.find({}).toArray(function (err, accs) {
        log.debug('test5');
        expect(err).to.not.exist;
        log.debug('test6');
        expect(accs.length).to.equal(1);
        return deferred.fulfill();
    });
    return deferred.promise;
};

log.debug('test3');

// Enqueue the testAccount function
browser.controlFlow().execute(testAccount);  
log.debug('test4');

Output is now what we expect :

test0

test1

test2

test3

test4

test5

test6
emragins
  • 4,607
  • 2
  • 33
  • 48
jmcollin92
  • 2,896
  • 6
  • 27
  • 49
  • How would the testX output be different if you called testAccount() directly (without the browser.controlFlow stuff)? – Mike Park Apr 16 '15 at 20:02
  • 1
    Additionally, why the need for browser.wait? – Mike Park Apr 16 '15 at 20:08
  • If testAccount is called directly sometimes you have the correct order and sometimes not depending of browser or machine velocity. You could have something like 0, 1, 2, 3, 5, 6, 4 or the test expect(message.isPresent()).to.be.eventually.true can be evaluated after the testAccount call – jmcollin92 Apr 18 '15 at 07:15
  • 1
    browser.wait(EC.visibilityOf(message), 5000); is a new feature un Protractor 1.7 that waits for the visibility of an element. This is usefull if your element can change visibility with a deferred animation for example. – jmcollin92 Apr 18 '15 at 07:18
0

Give yourself a favor, and avoid .then

Today, async/await is a lot more appropriate for handing promises

But in nutshell, open protractor API page https://www.protractortest.org/#/api, find the method you're about to use and see what it returns. If it says promise, just add await before calling it. Make sure, to make your wrapping function async

it('test case 1', async () => {
  await elem.click()
})
Sergey Pleshakov
  • 7,964
  • 2
  • 17
  • 40