Angular-mocks digest doesn't wait for promise

48 Views Asked by At

I want to have to function that returns a promise that resolves when the DOM is loaded:

function waitForDom() {
  return $q((resolve) => {
    $window.addEventListener('DOMContentLoaded', () => {
      resolve("yay, i've loaded");
    });
  })
})

This works just fine running in the browser. However, when I try to test it, the test never finishes as this promise never resolves:

it('some test', (done) => inject(($window, $q, $rootScope) => {
  waitForDom()
    .then(it => expect(it).toBe("yay, i've loaded"))
    .then(done);

  $rootScope.$digest();
}));

From what I understand of angular, promises don't resolve (and chain) until you call $digest. This makes it so that you can test angular in a synchronous fashion. I get it. However, the example that I have here should finish, but for some reason it always times out.

I've tried putting the resolve() inside $scope.$apply(), but I get "$digest already in progress".

I've tried putting the $digest inside setTimeout and that gives me a $digest already in progress too, which I'm really confused about. But that's not really the problem.

TLDR How do I get my test to actually finish?

Edit

While I can work around my above example, I'm looking for a general solution for when you resolve a promise inside the body of a DOM event listener, like this:

function appendIframe() {
  return $q((resolve) => {
    const iframe = $window.document.createElement('iframe');
    const iframeParent = $window.document.body;

    iframe.src = authUrl;

    iframe.onload = function() {
      resolve('iframe loaded')
    };

    iframeParent.appendChild(iframe);
  });
})

And no, doing this with jqlite doesn't make a difference.

1

There are 1 best solutions below

1
georgeawg On

Use angular.element:

function waitForDom() {
  return $q((resolve) => {
    angular.element( () => {
      resolve("yay, i've loaded");
    });
  })
})

From the Docs:

Most browsers provide similar functionality in the form of a DOMContentLoaded event. However, jQuery's .ready() method differs in an important and useful way: If the DOM becomes ready and the browser fires DOMContentLoaded before the code calls .ready( handler ), the function handler will still be executed. In contrast, a DOMContentLoaded event listener added after the event fires is never executed.