0

I have the following code:

const until = protractor.ExpectedConditions;
export class GenericPOClass {
    waitForPagetoLoad(numberOfSecondsToWait: number = 30) { // note the default
        (1) let elementToWaitFor = element(by.css('app-spec-mapper .stepper'));
        (2) browser.wait(until.presenceOf(elementToWaitFor), numberOfSecondsToWait*1000);
        (3) return expect(elementToWaitFor.isPresent()).toBeTruthy('Element stepper is not actually present.');
    }
}

This is code called from a protractor/jasmine test to wait for my non-angular web page to load.

If I use all 3 lines, with a "default wait time of 30 seconds, I make it past this loading part of my test and move onto another section.

If I use just lines 1 & 3, I get the expectation error.

If I use line 2 as well with a number of seconds to wait of 0, it works anyway. The page is loaded, line 3 passes.

Is there some type of default wait time on browser.wait that overrides my wait time? Is there some freaky magic of asynchronicity that it works if observed (browser.wait calling the expected conditions is magic)?

Coming from a synchronous Java and TestNG background, I've been learning protractor and JavaScript as I go, writing automated test cases for my job and things like this without a formal class or knowledgeable person to ask keep tripping me up. I've been wrangling with getting waiting for things to load working for a week, please help me understand this black magic.

Protractor Version: 5.1.2
NodeJS: 6.11.2
IDE: IntelliJ WebStorm

S.Huston
  • 254
  • 6
  • 18
  • 1
    Im not an expert on protractor, but any code that relies on the native javascript timeout function has to wait for other items in the browser event queue to finish before it executes. So it never executes immediately. The rendering engine likely executes a rendering cycle before the timeout callback triggers. A better in depth explenation: https://stackoverflow.com/a/779785/1320382 – Natalie Feb 26 '18 at 20:17

1 Answers1

1

The black magic you speak of is a Promise. And your three seconds is wait UP TO three seconds, not wait EXACTLY three seconds.

First, let's confirm it really is a promise we're dealing with, then we'll see how a promise works.

Looking at the protractor source, you'll see that until.presenceOf()1 returns ElementFinder.isPresent:

presenceOf(elementFinder: ElementFinder): Function {
  return elementFinder.isPresent.bind(elementFinder);
};

And ElementFinder.isPresent()2 returns a Promise containing a boolean

isPresent(): wdpromise.Promise<boolean> {
  return this.count().then((count) => {
    return count > 0;
  });
}

Ok, it's promise, but what how does it work? Here's a simplified version of what's happening in your test:

var wait = 0

// Count to three then change content to 'Times up'
var timeout = setTimeout(timesUp, wait)
function timesUp() {
  document.body.innerHTML = 'Times up'
}

// make a new Promise and keep a reference of its resolve function
var resolve
var p = new Promise(function(resolveFunc){
  resolve = resolveFunc
})

// when the Promise is resolved by calling resolve(), cancel the timer
p.then(cancelTimeout)
function cancelTimeout() {
  clearTimeout(timeout)
  document.body.innerHTML = 'Timeout cancelled'
}

// resolve the promise (like presenceOf finding the element)
resolve()

In the snippet, setTimeout asks the runtime to run timesUp() zero seconds after the current execution finishes. But before the end of the execution, resolve() is called, so we see 'Timeout cancelled', not 'Times up'.

In your test, the same happens. Your element is found before a future round of execution can fail the test and the test passes.

Actually, the 'execution' I referred to is called the 'event loop': https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#Event_loop

Hope that helps.

Clarence Lee
  • 141
  • 6
  • It makes more sense. I think I felt that this was happening, but I have a hard time articulating so I can investigate further. Thank you for the references and the explanation that it's happening in reference to an "event loop". I have sometime more concrete that I can study. :) – S.Huston Mar 06 '18 at 17:23