Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jasmine - Testing links via Webdriver I/O

I have been working on a end-to-end test using Webdriver I/O from Jasmine. One specific scenario has been giving me significant challenges.

I have a page with 5 links on it. The number of links actually challenges as the page is dynamic. I want to test the links to see if each links' title matches the title of the page that it links to. Due to the fact that the links are dynamically generated, I cannot just hard code tests for each link. So, I'm trying the following:

it('should match link titles to page titles', function(done) {
  client = webdriverio.remote(settings.capabilities).init()
    .url('http://www.example.com')
    .elements('a').then(function(links) {
      var mappings = [];

      // For every link store the link title and corresponding page title
      var results = [];
      for (var i=0; i<links.value.length; i++) {
        mappings.push({ linkTitle: links.value[0].title, pageTitle: '' });
        results.push(client.click(links.value[i])
          .getTitle().then(function(title, i) {
            mappings[i].pageTitle = title;
          });
        );
      }

      // Once all promises have resolved, compared each link title to each corresponding page title
      Promise.all(results).then(function() {
        for (var i=0; i<mappings.length; i++) {
          var mapping = mappings[i];
          expect(mapping.linkTitle).toBe(mapping.pageTitle);
        }
        done();          
      });                  
    });
  ;
});

I'm unable to even confirm if I'm getting the link title properly. I believe there is something I entirely misunderstand. I am not even getting each links title property. I'm definately not getting the corresponding page title. I think I'm lost in closure world here. Yet, I'm not sure.

UPDATE - NOV 24 I still have not figured this out. However, i believe it has something to do with the fact that Webdriver I/O uses the Q promise library. I came to this conclusion because the following test works:

it('should match link titles to page titles', function(done) {
  var promise = new Promise(function(resolve, reject) {
    setTimeout(function() { resolve(); }, 1000);
  });

  promise.then(function() {
    var promises = [];
    for (var i=0; i<3; i++) {
      promises.push(
        new Promise(function(resolve, reject) {
          setTimeout(function() {
            resolve();
          }, 500);
        })
      );
    }

    Promise.all(promises).then(function() {
      expect(true).toBe(true)
      done();
    });
}); 

However, the following does NOT work:

it('should match link titles to page titles', function(done) {
  client = webdriverio.remote(settings.capabilities).init()
    .url('http://www.example.com')
    .elements('a').then(function(links) {
      var mappings = [];

      // For every link store the link title and corresponding page title
      var results = [];
      for (var i=0; i<links.value.length; i++) {
        mappings.push({ linkTitle: links.value[0].title, pageTitle: '' });
        results.push(client.click(links.value[i])
          .getTitle().then(function(title, i) {
            mappings[i].pageTitle = title;
          });
        );
      }

      // Once all promises have resolved, compared each link title to each corresponding page title
      Q.all(results).then(function() {
        for (var i=0; i<mappings.length; i++) {
          var mapping = mappings[i];
          expect(mapping.linkTitle).toBe(mapping.pageTitle);
        }
        done();          
      });                  
    })
  ;
});

I'm not getting any exceptions. Yet, the code inside of Q.all does not seem to get executed. I'm not sure what to do here.

like image 722
Some User Avatar asked Sep 27 '22 04:09

Some User


1 Answers

Reading the WebdriverIO manual, I feel like there are a few things wrong in your approach:

  • elements('a') returns WebElement JSON objects (https://code.google.com/p/selenium/wiki/JsonWireProtocol#WebElement_JSON_Object) NOT WebElements, so there is no title property thus linkTitle will always be undefined - http://webdriver.io/api/protocol/elements.html
  • Also, because it's a WebElement JSON object you cannot use it as client.click(..) input, which expects a selector string not an object - http://webdriver.io/api/action/click.html. To click a WebElement JSON Object client.elementIdClick(ID) instead which takes the ELEMENT property value of the WebElement JSON object.
  • When a client.elementIdClick is executed, the client will navigate to the page, trying to call client.elementIdClick in the next for loop cycle with next ID will fail, cause there is no such element as you moved away from the page. It will sound something like invalid element cache.....

So, I propose another solution for your task:

  • Find all elements as you did using elements('a')
  • Read href and title using client.elementIdAttribute(ID) for each of the elements and store in an object
  • Go through all of the objects, navigate to each of the href-s using client.url('href'), get the title of the page using .getTitle and compare it with the object.title.

The source I experimented with, not run by Jasmine, but should give an idea:

    var client = webdriverio
        .remote(options)
        .init();

    client
        .url('https://www.google.com')
        .elements('a')
        .then(function (elements) {
            var promises = [];

            for (var i = 0; i < elements.value.length; i++) {
                var elementId = elements.value[i].ELEMENT;

                promises.push(
                    client
                        .elementIdAttribute(elementId, 'href')
                        .then(function (attributeRes) {
                            return client
                                .elementIdAttribute(elementId, 'title')
                                .then(function (titleRes) {
                                    return {href: attributeRes.value, title: titleRes.value};
                                });
                        })
                );
            }

            return Q
                .all(promises)
                .then(function (results) {
                    console.log(arguments);
                    var promises = [];
                    results.forEach(function (result) {
                        promises.push(
                            client
                                .url(result.href)
                                .getTitle()
                                .then(function (title) {
                                    console.log('Title of ', result.href, 'is', title, 'but expected', result.title);
                                })
                        );
                    });

                    return Q.all(promises);
                });
        })
        .then(function () {
            client.end();
        });

NOTE:

  • This fails to solve your problem, when the links trigger navigation with JavaScript event handlers not the href attributes.
like image 99
tiblu Avatar answered Sep 29 '22 06:09

tiblu