Question

the code below finds a webElement with protractor (an authentication button) and trys to resolve a promise with that element as a parameter.

function someFunc() {
    var deferred = q.defer();
    browser.findElement(by.css('[data-aid="buttons.authenticate"]')).then(
        function(e) {
            console.log('element found: ' + e);
            deferred.resolve(e);
        }
    );
    return deferred.promise;
}

someFunc().then(
        function(elem) {
            console.log('got elem ' + elem);
            elem.click();
        }
    );

output: element found: [object Object]
so the then function is never called. when i replace the resolve() with some other value (int, string, array, map) it works fine. so why can't i return a WebElement object?

thank you

Was it helpful?

Solution

To actually do what you are asking, I suspect you should use instead of q, the Webdriver Promise API, exposed as protractor.promise

var deferred = protractor.promise.defer();

which uses a fulfill function and not resolve:

deferred.fulfill(e);

However...

If you're creating a promise via defer just to do something with one or more existing promises, that is often a sign you're not using promise chaining, or available API functions, when you could. Chaining can be done by returning a) what you want to resolve from the then callback and b) returning the result of the then function:

function someFunc() {
  return browser.findElement(by.css('[data-aid="buttons.authenticate"]')).then(function(e) {
    console.log('element found: ' + e);
    return e;
  });
}

someFunc().then(function(elem) {
  console.log('got elem ' + elem);
  elem.click();
});

But because in this example, you're returning exactly the fulfilled value of the promise, this could be simplified even further:

function someFunc() {
  return browser.findElement(by.css('[data-aid="buttons.authenticate"]'));
}

someFunc().then(function(elem) {
  console.log('got elem ' + elem);
  elem.click();
});

And, because click is actually exposed on the promise result of findElement, this could be further simplified to

browser.findElement(by.css('[data-aid="buttons.authenticate"]').click();

You say in the comments you can't do the above, since you want to "wrap" the promise. It's not too clear what that means, but say you want to have 2 elements available in order to do something with them, you can use protractor.promise.all, and not manually create a promise via defer:

var button1 = browser.findElement(by.css('[data-aid="buttons.authenticate"]');
var button2 = browser.findElement(by.css('[data-aid="buttons.confirm"]');

protractor.promise.all([button1, button2]).then(function(buttons) {
  var button1 = buttons[0];
  var button2 = buttons[1];
  // Do something with both buttons   
});

There are other functions that can manipulate promises in various ways available Webdriver's Promise API. 'map' and 'filter' I think are ones I have used.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top