Adam
Adam

Reputation: 2077

Protractor variable scope with promises

Background

I'm working on an Angular app which uses ng-repeat to make a table. One of the users found that the table sometimes contains duplicate entries, which I confirmed visually, then promptly wrote a Protractor test for.

The Test

enter image description here

Variable Scoping Issues

While writing the test, I noticed that the scope wasn't behaving in a way that I understood.

Naturally, the for-loop on line 61 has access to linkStorage (line 38), since it is in a higher scope. It logs that all of the objects have been successfully added to the object via the for-loop in the promise on line 47.

However, when I move the confirmation loop outside of the promise, say, before the expect block...

enter image description here ...linkStorage is an empty object.

enter image description here

Looping over the object finds no nested key-value pairs; it is truely empty.

Question (tl;dr)

Why is the linkStorage object populated inside the then statement, but not before the expectation?

Upvotes: 3

Views: 1487

Answers (1)

Adam
Adam

Reputation: 2077

Asynchronousity Strikes Again

The first example works is due to asynchronousity. Because the .getAttribute method is non-blocking, the code continues to run past it while it works. Therefore, the console loop is reached before the object has been populated; it's empty.

If you give the asynchronous code some time to run, maybe one second:

enter image description here

...linkStorage is populated.

Complete Solution

Chain multiple promises together to ensure code runs at the correct time.

it('should not have duplicates within the match grid', function() {
    // Already on job A, with match grid shown.
    var duplicate = false;
    var linkStorage = {};

    // Save unique links
    var uniqueUserLinks = element.all(by.css('div.row table tbody tr td a'));
    
    // get an array of href attributes
    uniqueUserLinks.getAttribute('href')
      .then(function(hrefs) {
        // add the links to the linkStorage object
        for (var i = 0; i < hrefs.length; i++) {
          // if the link is already there
          if( linkStorage[ hrefs[i] ] ) {
            // update its counter
            linkStorage[hrefs[i]] += 1
            duplicate = true;
            // there's already one duplicate, which will fail the test
            break;
          } else {
            // create a link and start a counter
            linkStorage[hrefs[i]] = 1;
          }
        };
      }).then(function() {
        // confirm links have been added to storage
        for(var link in linkStorage) {
          console.log('link:', link );
          console.log('number:', linkStorage[link] );
        }
      }).then(function() {
          expect(duplicate).toBe(false);
      });
  });

Upvotes: 3

Related Questions