2

I have a Protractor call that returns a promise, and the promise wraps a value.

var e = element.all(by.css("selector")).first();

e.getAttribute('id').then(function (text) {
  console.log(text); 
});

I want to do a blocking wait on the promise returned from getAttribute() and retrieve the resulting text.

This needs to be a blocking function; adding then would require rewriting many of our existing tests. How can I write this?

Edit:

What I am trying to do is to retrieve a value from the page and use it to build an element selector. So:

  1. Find the dynamic ID of element one: element.all(by.css("...")).first().getAttribute('id')
  2. Build a selector based on that value: var elementSelector = '#X' + elementOneID + '-Y';
  3. Find and do something with element two: element(by.css(elementSelector))...

I will accept any answer that allows me to do this.

Edit 2:

Apparently this is not possible without a callback. I ended up revising my CSS selectors.

Community
  • 1
  • 1
TrueWill
  • 25,132
  • 10
  • 101
  • 150
  • I don't think I have to go too far out on a limb saying that this is impossible. – Bergi Aug 06 '15 at 23:06
  • Have you tried to apply similar logic with event emitter or **async**? – Dejan Toteff Aug 07 '15 at 17:45
  • 1
    I saw this project out there. Could be helpful? https://www.npmjs.com/package/webdriver-sync – jeremysawesome Aug 07 '15 at 18:19
  • @jeremysawesome That is brilliant! Unfortunately we use Angular and have a large body of Protractor tests. – TrueWill Aug 07 '15 at 18:27
  • Would something like this work? `function getAttributeValueGivenSelector(selector, attribute){ var e = element.all(by.css("selector")).first(); var result = e.getAttribute(attribute); browser.wait(result); return result; }` – jeremysawesome Aug 07 '15 at 19:01
  • @jeremysawesome The function you provided returns a promise. – TrueWill Aug 08 '15 at 16:01
  • 1
    @TrueWill - I wonder. Would passing in a callback method help make it so you don't have to rewrite all your tests? Like if the test method were the callback? Then you could do something like `getAttributeValueAndExecuteCallback(selector, attribute, callback){ // ... code .then(callback(attributeValue)); //... code }` It feels like that could be easier than re-writing all your tests? But I'm not sure without actually seeing them. Let me know how it turns out! – jeremysawesome Aug 09 '15 at 05:04
  • @jeremysawesome Thanks - good idea. We found a way to keep the ID static on most tests, reducing the ones to rewrite from 25 to 3. – TrueWill Aug 09 '15 at 21:48

2 Answers2

1

You mean this?

var e = element.all(by.css("selector")).first();
browser.wait(function() {
        return e.getAttribute('id').then(function (id) {
              if(id === 'oneIWant') {
                  return true;
              } else {
                  return false;
              }
          });
},3000,'Waiting for the id value to be something awesome');

Edit1

From WebDriverJS documentation here you can find below quote. So if you have a series of selenium-webdriver commands, they will be pushed to the promise manager queue and they will be executed one after the other. When you say wait is not blocking the next command, it certainly means that your wait condition is incorrect.

The promise manager maintains a queue of scheduled tasks, executing each once the one before it in the queue is finished. The WebDriver API is layered on top of the promise manager

Edit2

Please check protractor expected conditions here

var EC = protractor.ExpectedConditions;

var e = element.all(by.css("...")).first();
browser.wait(EC.presenceOf(e), 10000);

if(e.isPresent()) {
  e.getAttribute('id').then(function(elementOneID) {
     var elementSelector = '#X' + elementOneID + '-Y';
     var el = element(by.css(elementSelector));
     browser.wait(EC.presenceOf(el), 10000);
     el.click(); //or do stuff
  });
}
nilesh
  • 14,131
  • 7
  • 65
  • 79
  • This is close, but I need the result of `getAttribute()` after `wait` returns (I don't know what it is; this is a simplified example). I'm assuming I'd have to close over a variable from the outer scope and just return true? – TrueWill Aug 07 '15 at 12:54
  • @TrueWill You could certainly do that. – nilesh Aug 07 '15 at 16:48
  • or rather thats the only option you have IMHO – nilesh Aug 07 '15 at 17:43
  • It's not working. I think the [Protractor Control Flow](https://github.com/angular/protractor/blob/master/docs/control-flow.md) is messing things up. If I just wanted to `expect` on it I'd be fine, but I need the value to build the selectors to find my elements. – TrueWill Aug 07 '15 at 18:20
  • In short I'm dealing with this: http://stackoverflow.com/questions/30949576/protractor-wait-for-async-promise-before-doing-next I expected `wait` to effectively block until completion, and that does not appear to be the case. – TrueWill Aug 07 '15 at 18:35
  • Accepted answer in that question, exactly matches my answer. It definitely looks like your wait condition is incorrect. If you simplify and share your code, it will be easy to point something obvious. I also added some doc reference in recent edit. Check it out! – nilesh Aug 07 '15 at 21:22
  • Please see the edit to my question describing what I am trying to do. – TrueWill Aug 08 '15 at 15:50
  • Thanks - it looks like that is the only way. I am disappointed in Protractor, as this means existing tests will need to be rewritten. – TrueWill Aug 08 '15 at 21:17
-1

You can't actually block. Since JavaScript is single threaded, nothing can happen while you wait. Protractor will never resolve the promise if you were to block. see this answer to a similar question

You can get the appearance of blocking on promises with es7's async await, but that feature is only available if you use a transpiler such as babel.

But, I believe @nilesh's answer is what you are looking for. browser.wait will basically run a function over and over until it returns true, or it times out, but it will not block the thread.

Community
  • 1
  • 1
lyjackal
  • 3,984
  • 1
  • 12
  • 25