Дайджест Angular-mocks не ждет обещаний

Я хочу иметь функцию, которая возвращает обещание, которое разрешается при загрузке DOM:

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

Это прекрасно работает в браузере. Однако, когда я пытаюсь его протестировать, тест никогда не завершается, так как это обещание никогда не разрешается:

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

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

Из того, что я понимаю в angular, обещания не разрешаются (и не связываются), пока вы не вызовете $digest. Это позволяет тестировать angular синхронно. Я понимаю. Однако пример, который у меня здесь, должен завершиться, но по какой-то причине он всегда истекает.

Я пытался поместить resolve() внутрь $scope.$apply(), но получаю сообщение "$digest уже выполняется".

Я попытался поместить $digest внутрь setTimeout, и это дало мне $digest, который уже выполняется, что меня действительно смущает. Но проблема не в этом.

TLDR Как добиться завершения теста?

Изменить

Хотя я могу обойти приведенный выше пример, я ищу общее решение, когда вы разрешаете обещание внутри тела прослушивателя событий DOM, например:

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);
  });
})

И нет, делать это с jqlite не имеет значения.


person shoebox639    schedule 15.12.2017    source источник
comment
Вы дважды проверили, что событие DOMContentLoaded действительно срабатывает, когда вы запускаете тест?   -  person JLRishe    schedule 15.12.2017
comment
Ага. Когда я вхожу в обратный вызов события, он всегда в конечном итоге печатается.   -  person shoebox639    schedule 15.12.2017


Ответы (1)


Используйте angular.element:

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

Из документов:

Большинство браузеров предоставляют аналогичную функциональность в виде события DOMContentLoaded. Однако метод jQuery .ready() отличается важным и полезным моментом: если DOM становится готовым и браузер запускает DOMContentLoaded до того, как код вызовет .ready(handler), обработчик функции все равно будет выполняться. Напротив, прослушиватель событий DOMContentLoaded, добавленный после срабатывания события, никогда не выполняется.

person georgeawg    schedule 15.12.2017
comment
В итоге я сделал столько же, чтобы исправить эту часть. Однако это не совсем касается основной проблемы, с которой я столкнулся. Это работает с обещаниями и событиями, но у меня есть другое событие позже в цепочке, которое разрешается в событии onLoad iframe. Я не могу использовать этот обходной путь там. - person shoebox639; 16.12.2017