Не могу дождаться окончания DOM-рендеринга в модульном тесте Angular / Jasmine - PullRequest
0 голосов
/ 02 марта 2020

У меня есть компонент диаграммы Angular p ie, построенный через VegaEmbed (https://github.com/vega/vega-embed), который использует Vega и D3 в качестве основных графических зависимостей. Это делает от предоставления заголовка и некоторых (ключ, значение) пар. Я выделил этот компонент и изменил main.ts, чтобы запустить Jasmine из Stackblitz, чтобы поделиться с вами. В этом тесте я проверяю, что диаграмма p ie действительно отображает теги SVG <text> для значений «30%» | «70%» и легенда «Совместный генеральный директор / Председатель» | «Отдельный генеральный директор / председатель». Однако кажется, что они запускаются слишком рано, и VegaEmbed + Vega + D3 все еще заняты созданием этого SVG. (Я сделал вывод, что тестировать, просто заглянув в DOM с помощью Chrome dev).

enter image description here

https://stackblitz.com/edit/angular-d3-pie-chart-unit-test

Я пробовал ряд вещей: async, FakeAsync + tick, jasmine.clock, изменение логики обещаний c в моем Angular компоненте, et c. .. fixture.whenStable приближает меня на шаг, но texts объявленная строка 50 все еще не определена.

Я не знаю, как работают внутренние компоненты Vega, VegaEmbed и D3. Если эти библиотеки не используют обещания, скорее старомодные обратные вызовы, то Angular Зоны могут не ждать достаточно в течение async?

Что меня немного смущает, так это то, что console.log(texts); в конечном итоге показывает Коллекция из 4 текстовых элементов SVG в консоли. Тем не менее, console.log(texts.length); отображает 0!

  1. Как это может быть?
  2. Как мне заставить мой тестовый код ждать до того момента, когда D3 закончит рисовать SVG, и только тогда запускать операторы expect?

1 Ответ

1 голос
/ 02 марта 2020

Это хороший вопрос, у меня есть похожие проблемы с Ag-Grid, когда мне приходится ждать завершения рендеринга или его обратных вызовов, прежде чем я сделаю утверждения, и нет хорошего способа, как вы упомянули с fakeAsync, async/done, et c. По крайней мере, ничего из того, что я нашел.

Я нашел способ сделать служебную функцию, например, так:

import { interval } from 'rxjs';
.....
export const waitUntil = async (untilTruthy: Function): Promise<boolean> => {
  while (!untilTruthy()) {
    await interval(25).pipe(take(1)).toPromise();
  }
  return Promise.resolve(true);
};

waitUntil будет повторять цикл каждые 25 мс, пока не будет предоставлена ​​функция обратного вызова. это правда Количество времени зависит от вас.

Итак, в ваших тестах вы можете сделать что-то вроде:

it('should render the chart', async(done) => {
  // make your arrangements
  // do your action
  fixture.detectChanges();
  // wait for promises to resolve (optional)
  await fixture.whenStable();
  await waitUntil(() => /* put a condition here that will resolve to a truthy value 
  at a later time where the rest of the assertions rely on 
  it such as the graph being present with its labels*/);
  // the rest of your assertions of what should be there what should not
  done(); // call done to let the test this async test is done
});

Вы упомянули setTimeout, работающую со значением 0. Это работает, потому что мы помещаем то, что находится внутри setTimeout, в конец очереди стека вызовов, потому что она работает асинхронно. Делать это таким образом все еще хорошо, но мне нравится, как тесты читаются с подходом waitUntil.

...