Machtyn
Machtyn

Reputation: 3272

How to get nested JSON from promise in protractor

I am testing a page which outputs some data and I want to capture that data into a JSON object. The data appears as follows (in html)

<cards>
  <card>
    <div id="name">Joe</div>
    <div id="web">
      <a target="_blank" href="http://joessite.com">joessite.com</a>
    </div>
  </card>
  <card>
    <div id="name">Frank</div>
    <div id="web">
      <a target="_blank" href="http://frankssite.com">frankssite.com</a>
    </div>
  </card>
</cards>

I want my resulting JSON object to be:

[ {
    name: "Joe",
    web: {
        text: "joessite.com",
        link: "http://joessite.com"
    }
  },
  {
    name: "Frank",
    web: {
        text: "frankssite.com",
        link: "http://frankssite.com"
    }
  } ]

So I wrote the following code:

var cardDataToJSON = function() {
    var result = {};

    $$('cards card').each(function (card) {
        card.$('#name').getText().then(function (text) { result["name"] = text; });

        var web = {};
        card.$('#web').getText().then(function (text) { 
            web["text"] = text; 
        });
        card.$('#web').getAttribute('href').then(function (href) { 
            web["href"] = href; 
        });

        result.web = web;
    });

    return result;
};

Unfortunately, my actual result is (note the first "web" is blank).

[ {
    name: "Joe",
    web: { }
  },
  {
    name: "Frank",
    web: {
        text: "frankssite.com",
        link: "http://frankssite.com"
    }
  } ]

What am I doing wrong or what do I need to improve in my code?

(I'm using protractor version 3.2.2)

Upvotes: 3

Views: 346

Answers (2)

alecxe
alecxe

Reputation: 473853

I would use map() with protractor.promise.all():

var cardDataToJSON = function() {
    return $$('cards card').map(function (card) {
        var name = card.$('#name').getText(),
            web = card.$('#web a').getText(),
            href = card.$('#web a').getAttribute('href');

        return protractor.promise.all([name, web, href]).then(function (values) {
            return {
                name: values[0],
                web: {
                    text: values[1],
                    link: values[2]
                }
            }
        });
    });
};

Upvotes: 2

Florent B.
Florent B.

Reputation: 42518

It looks like a synchronization issue between the promises. Another way would be to make a JavaScript call which would also noticeably reduce the execution time:

browser.executeScript(function() {
  return [].map.call(document.querySelectorAll('cards > card'), function(card) {
    return {
      name: card.querySelector('#name').textContent,
      web: {
        text: card.querySelector('#web a').textContent,
        link: card.querySelector('#web a').getAttribute('href')
      }
    };
  });
}).then(function(result){
  console.log(result);
});

Upvotes: 1

Related Questions