tl; dr Protractor может помочь вам выявить проблемы с NgZone и, таким образом, косвенно помочь вам повысить производительность вашего приложения!

Мы знаем Транспортир как инструмент тестирования для автоматизации тестирования e2e, это очень здорово выполнять свою работу, но в некоторых случаях, как этот, он может даже помочь вам найти проблемы с производительностью вашего приложения.

Проблема

Несколько дней назад мы доработали наше приложение и облегчили скучную работу по контролю качества нашего приложения. Но я обнаружил проблему в своих тестах e2e, Protractor жаловался только на одной странице нашего приложения с ошибкой:

Failed: Timed out waiting for asynchronous Angular tasks to finish after 15 seconds. This may be because the current page is not an Angular application. Please see the FAQ for more details: https://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular
 While waiting for element with locator — Locator: By(css selector, i-floating-icon)

В моей спецификации отсутствует строка:

browser.wait(EC.presenceOf($(‘i-floating-icon’)), 2000);

И даже изменив его на

browser.wait(EC.presenceOf($(‘html’)), 2000);

проблема все еще возникала и начала сводить меня с ума!

Отладка

Итак, я прочитал документацию по ссылке, которую Protractor дал мне в сообщении об ошибке, и могу прочитать это:

Для приложений Angular Protractor будет ждать, пока зона Angular не стабилизируется. Это означает, что длительные асинхронные операции заблокируют продолжение вашего теста. Чтобы обойти это, запустите эти задачи вне зоны Angular.

И еще я завел своего любимого друга: вкладку производительности Chrome DevTools. Я записал 5 секунд бездействия на этой странице, и вот результат:

Для сравнения, вот профиль аналогичной страницы, но без проблем при выполнении теста e2e:

Решение

Поэтому я начал комментировать все компоненты в своем приложении, чтобы найти тот, который вызывает бесконечное тикание NgZone, и нашел плохого парня. Я использую hls.js, и я помню, что где-то читал (???), что сторонняя библиотека, выполняющая тяжелые вещи, должна выполняться вне контекста Angular для повышения качества… и сегодня я понимаю, почему.

Итак, в моем коде есть:

const hls = new Hls();

hls.loadSource(src);
hls.attachMedia(videoEl);
hls.on(Hls.Events.MANIFEST_PARSED, () => this.playAfterLoad(videoEl));

И просто окружите это runOutsideAngular:

this.ngZone.runOutsideAngular(() => {
    const hls = new Hls();

    hls.loadSource(src);
    hls.attachMedia(videoEl);
    hls.on(Hls.Events.MANIFEST_PARSED, () => {
        this.ngZone.run(() => {
            this.playAfterLoad(videoEl);
        })
    });
});

=

А транспортир? о, не волнуйтесь, он тоже счастлив, и все тесты проходят успешно!