Перейти к содержимому

Переход на 5.0 ​

В 5.0 поменялись два диапазона peer-зависимостей: Angular >=20 и rxjs >=7.2. В самой 5.0 хелперы, опции и поведение спаев не изменились. Большинству проектов достаточно поднять версию:

bash
npm install -D vitest-auto-spy@5   # pnpm: pnpm add -D vitest-auto-spy@5

Что проверить

  1. Если вы используете точку входа для Angular, а версия Angular ниже 20, сначала обновите Angular.

  2. Если у вас pnpm и vitest-auto-spy/angular не находит @angular/platform-browser, добавьте его в devDependencies.

  3. Если вы намеренно закрепили rxjs@7.0 или 7.1, перейдите на 7.2 или новее. В Angular-проектах она уже стоит.

  4. Переходите сразу на более позднюю 5.x (обычный случай)? Тогда ещё:

    • запустите npx vitest-auto-spy doctor: он назовёт каждый импорт, который в 5.21.0 ушёл из vitest-auto-spy/angular (диагностика, подмены диалогов и платформы, матчеры);
    • запустите проверку типов: с 5.1.0 стабы типизируются по методу;
    • запустите тесты: несколько исправленных хелперов теперь роняют спеки, которые проходили случайно.

    Подробно — Изменения в следующих релизах 5.x.

Зачем обновляться ​

Хелперы, опции и поведение в рантайме не изменились. Изменились диапазоны peer-зависимостей — то, на чём пакет заявляет, что работает. Оба старых диапазона обещали версии, на которых код работать не мог.

Что вы получаетеПроверено
Диапазон Angular говорит правду. >=16.0.0 допускал четыре мажора, на которых точка входа даже не загружается.Angular 16–22 скачаны, их списки экспортов разобраны: ɵSIGNAL впервые есть в 18, provideZonelessChangeDetection — в 20
@angular/platform-browser находится под pnpm. /angular импортирует из него By, но пакет не был объявлен peer-зависимостью.не объявлен → необязательная peer-зависимость с тем же диапазоном >=20, что у двух других
Диапазон rxjs больше не обещает rxjs 8. Observable-хелперы импортировали шесть операторов из rxjs/operators, а в rxjs 8 этого пути нет.все шесть теперь берутся из корня rxjs, который экспортирует их с 7.2 (проверено по декларациям типов самого rxjs 7.2.0)
flushEffects() стал проще. Запасного пути через ApplicationRef.tick() для старого Angular больше нет.запасной путь удалён, покрытие по-прежнему 100 %

Почти для всех цена — одно число в package.json. Все мажоры Angular, которые Angular ещё поддерживает, проходят новую нижнюю границу. Все Angular-проекты уже подходят под границу rxjs.

Что изменилось ​

Два ломающих изменения, оба в peer-диапазонах:

  • ни один хелпер не удалён и не переименован;
  • ни одна опция не поменяла смысл;
  • ни один спай не ведёт себя иначе.

Если вы переходите с 4.x сразу на более позднюю 5.x, появятся и ошибки типов из 5.1.0: этот релиз проверяет, что возвращает стаб. Раздел Изменения в следующих релизах 5.x разбирает их по тексту ошибки.

Что делать
1. Angular — >=20, и Angular-пакетов в peer-зависимостях теперь триобновить Angular, если он ниже 20; добавить @angular/platform-browser, если жалуется pnpm
2. rxjs — >=7.2ничего, если вы не закрепили rxjs@7.0 или 7.1 намеренно

1. Angular — >=20, и Angular-пакетов в peer-зависимостях теперь три ​

jsonc
"peerDependencies": {
  "@angular/common": ">=20.0.0",
  "@angular/core": ">=20.0.0",
  "@angular/platform-browser": ">=20.0.0", // новое: раньше не объявлялся
  // …
}

Все три — необязательные peer-зависимости. Проект, который не импортирует ни одну точку входа для Angular, не ставит ни одного из них.

Почему 20, а не что-нибудь пониже ​

Пакет импортирует два символа Angular как значения. Если такого импорта нет, не загружается вся точка входа, а не один хелпер.

  • ɵSIGNAL есть с Angular 18. Его читает runEffect(), и точка входа /angular импортирует его при загрузке. На Angular 16 или 17 падает вся точка входа, а с ней provideAutoSpy и injectSpy.
  • provideZonelessChangeDetection есть с Angular 20. В 18 и 19 функция называлась provideExperimentalZonelessChangeDetection, в 16 и 17 её не было. vitest-auto-spy/bun-angular импортирует её по имени, поэтому ниже 20 bun test падает при загрузке preload-файла, до запуска спек. Именно этот символ ставит границу на 20. На 18 и 19 точка входа /angular загружается нормально, не загружается только /bun-angular.

Для таблицы ниже @angular/core, @angular/common и @angular/platform-browser скачаны в версиях 16.2.12, 17.3.12, 18.2.14, 19.2.25, 20.3.30, 21.2.22 и 22.1.5. Разобраны их настоящие списки экспортов; заметкам к релизам не доверяли.

                                 16 17 18 19 20 21 22
ɵSIGNAL                           .  .  y  y  y  y  y
provideZonelessChangeDetection    .  .  .  .  y  y  y
provideExperimentalZonelessCD     .  .  y  y  .  .  .
TestBed.tick()                    .  .  .  .  y  y  y
InputSignal / ModelSignal         .  y  y  y  y  y  y
the other 29 symbols this code imports
                                  y  y  y  y  y  y  y

Ничего из того, что ещё поддерживалось, не отрезано ​

Angular поддерживает каждый мажор шесть месяцев активно и ещё двенадцать месяцев в режиме LTS. Когда ставили эту границу, LTS Angular 19 закончился 2026-05-19, а 16–18 — ещё раньше. Angular 20 — самый старый мажор, который Angular ещё поддерживает, и он же техническая граница этого пакета. Поэтому изменение не отрезает ничего, что ещё получает исправления.

мажорвыпускконец LTSстатус
162023-05-032024-11-03EOL
172023-11-082025-05-08EOL
182024-05-222025-11-22EOL
192024-11-192026-05-19EOL
202025-05-282026-11-28LTS; новая граница
212025-11-192027-05-19LTS
222026-06-032027-12-03активная поддержка

@angular/platform-browser впервые становится peer-зависимостью ​

vitest-auto-spy/angular импортирует из него By: на нём построены матчеры директив. Preload для Bun запускается через platformBrowserTesting(). Ни один из этих импортов не был объявлен.

  • С npm и его «поднятым» node_modules всё работало случайно: в любом Angular-проекте этот пакет есть.
  • С изолированной раскладкой pnpm пакет не находился вообще, и npm ls не подсказывал почему.

Если установка у вас работает, для вас ничего не меняется. Если у вас pnpm и /angular не находил @angular/platform-browser, этот релиз это чинит.

Это не про Angular 20

platformBrowserTesting() и BrowserTestingModule из @angular/platform-browser/testing иногда называют возможностями Angular 20+. Это не так: они экспортируют одни и те же имена во всех мажорах начиная с 16. В 20 изменилось другое: @angular/platform-browser-dynamic/testing перестал быть рекомендуемым путём. Граница bun-angular зависит только от provideZonelessChangeDetection.

Верхней границы нет, и это сознательно ​

Диапазон вроде >=20.0.0 <23.0.0 выглядит надёжнее, потому что ɵSIGNAL — приватный символ Angular. Но это неверное решение:

  • оно требует релиза этого пакета под каждый мажор Angular;
  • оно даёт ERESOLVE всем, кто обновил Angular первым;
  • оно ничего не защищает: приватный символ Angular может убрать и в минорной версии.

Настоящая защита — в коде: читать символ в рантайме, а не импортировать по имени. Тогда Angular без ɵSIGNAL сломает один хелпер, а не всю точку входа. Хелперы пакета для zone и zoneless уже так устроены, и runEffect() переходит на тот же подход.

2. rxjs — >=7.2, потому что глубокого пути импорта больше нет ​

jsonc
"peerDependencies": {
  "rxjs": ">=7.2.0" // было >=7.0.0
}

Observable-хелперы используют шесть операторов: concatMap, delay, switchMap, take, takeUntil и takeWhile. Они импортировались из rxjs/operators — старого глубокого пути. В rxjs 8 этого пути нет. Значит, >=7.0.0 обещал мажор, в котором код даже не загрузится.

Теперь импорт идёт из корня rxjs, который экспортирует все шесть операторов с 7.2. Это проверено по декларациям типов самого rxjs 7.2.0, а не по его changelog. firstValueFrom, из-за которого раньше стояла граница 7.0, не изменился. 7.2 — первая версия, где каждый символ, нужный пакету, есть по тому пути, откуда пакет его импортирует.

Кому нужно что-то делать: только проекту, который намеренно закрепил rxjs@7.0 или 7.1. На rxjs 7 Angular 16–22 требуют ^7.4.0, а это выше границы, так что Angular-проекту делать нечего. Верхней границы здесь тоже нет: rxjs 8 теперь действительно достижим.

Как теперь выглядит flushEffects() ​

Это не ломающее изменение. flushEffects() теперь всегда использует TestBed.tick(), который есть с Angular 20. TestBed.tick() обновляет и представления фикстур, которые не подключены к ApplicationRef. Старый запасной путь через ApplicationRef.tick() был нужен только версиям Angular, которые пакет больше не поддерживает, поэтому его убрали.

ts
import { flushEffects } from 'vitest-auto-spy/angular';

store.filter.set('open');
flushEffects(); // выполняет отложенные эффекты в сервисах и сторах, фикстура не нужна

Изменения в следующих релизах 5.x ​

Разделы выше описывают саму 5.0. Если вы переходите с 4.x сразу на более позднюю 5.x, вас коснутся и эти изменения.

Стабы проверяются по типу (5.1.0) ​

С 5.1.0 мок метода-спая — это MockInstance<Method>, а не MockInstance без аргумента типа (то есть (...args: any[]) => any). Теперь по методу типизируются:

  • mockReturnValue, mockImplementation, mockResolvedValue и их варианты Once;
  • параметры, которые получает реализация;
  • mock.calls.

В рантайме ничего не меняется.

Замер на переходе 4.6 → 5.24 Angular-приложения с 850 файлами спек: 61 ошибка в 22 файлах, ни одна не меняет поведение в рантайме. 59 из них укладываются в восемь видов; последние два были ошибками самого пакета:

Текст ошибкиСколькоЧто этоКак исправить
… with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' …21тип стаба отличается от типа метода: ?: T против ?: T | undefined, или записано key: undefinedсоздать subject из метода (returnSubject()); уберите ключ вместо записи key: undefined
Argument of type 'Spy<X>' is not assignable to parameter of type 'X'9спай возвращается из другого спаяmockReturnValue(asInstance(double))
Argument of type 'Observable<A>' is not assignable to parameter of type 'Observable<B>'8фикстура не совпадает с тем, что возвращает метод: DOM-Event вместо Event роутера, null вместо значенияисправить фикстуру — это и есть находка
Types of parameters 'x' and 'y' are incompatible в mockImplementation5аннотация параметра в реализации расходится с методомубрать аннотацию, тип даст метод
TS4111 или TS2375 внутри тела mockImplementation6раньше тело получало параметры any, и в нём ничего не проверялосьобычное исправление этой ошибки: тело теперь типизировано
Argument of type 'Partial<X>', самописный MockXType или базовый класс там, где нужен X4вручную собранный частичный объект там, где метод возвращает классcreateSpyFromClass(X) или createAutoMock<X>(), затем asInstance
TS2349: This expression is not callable на значении из mock.calls4mock.calls типизирован; слушатель из addEventListener имеет тип EventListenerOrEventListenerObjectсузить тип (typeof listener === 'function') перед вызовом
… is not assignable to parameter of type 'RxMethod<…>' внутри mockReadonlyProp(…, vi.fn(impl))2vi.fn(impl) берёт тип у заменяемого члена, а реализация возвращает не товернуть то, что возвращает член: Object.assign(vi.fn(), { destroy: vi.fn() }) для rxMethod

Два самых частых вида — до и после:

ts
// `Subject` инвариантен: аргумент типа должен точно совпадать с методом, `?` к `?`
const events$ = new Subject<EventData<{ params?: EventDataParams }>>(); // ❌ `on` объявляет `params?: EventDataParams | undefined`
remoteRendering.on.mockReturnValue(events$);

const events$ = remoteRendering.on.returnSubject(); // ✅ тип берётся из `on`, что бы он ни объявлял
ts
overlay.create.mockReturnValue(overlayRef); // ❌ у Spy<OverlayRef> нет приватных членов OverlayRef
overlay.create.mockReturnValue(asInstance(overlayRef)); // ✅ тот же объект с типом класса

Один вид был ошибкой пакета, а не спеки. @ngrx/signals типизирует каждый nullable-объект в сторе как объединение сигнатур вызова, например DeepSignal<Angle> | Signal<null>. С 5.1.0 по 5.24.0 такой член не принимал реализацию вообще: mockImplementation(() => angle()) не подходил ни под одну половину. 5.25.0 типизирует стаб как одну сигнатуру, которая возвращает объединение. Обновитесь, а не приводите типы (2 из 61 выше).

Не отвечайте ни на одну из этих ошибок через as any, as unknown as X или @ts-expect-error. Каждый такой приём выключает ту проверку, ради которой строка написана, а 8 из 61 ошибки были фикстурами, не совпадающими с тем, что возвращает метод.

Экспорты ушли из vitest-auto-spy/angular (5.21.0) ​

В 5.21.0 32 экспорта ушли из vitest-auto-spy/angular в три более узкие точки входа. Спека, которая импортирует provideAutoSpy, больше не загружает диагностику TestBed и подмены диалогов Material, а /angular стал на 14,3 % меньше. Большинство проектов меняют по одной строке импорта на каждую переехавшую группу, в одном setup-файле:

Импорт из vitest-auto-spy/angularТеперь импортируйте из
enableAngularDiagnostics, disableAngularDiagnostics, assertNoPendingRequests, assertNoShadowedProviders, AngularDiagnosticsOptionsvitest-auto-spy/angular/diagnostics
enableTestBedDiagnostics, disableTestBedDiagnostics, instrumentTestBed, getTestBedTiming, formatSpecTiming, reportSpecTiming, SpecTiming, TestBedDiagnosticsOptionsvitest-auto-spy/angular/diagnostics
createMatDialogRef, injectMatDialogRef, provideMatDialogData, provideMatDialogRef, DialogComponent, DialogRefLike, DialogResult, MatDialogRefDouble, MatDialogRefInitvitest-auto-spy/angular/doubles
createWindowDouble, createDocumentDouble, provideWindowDouble, provideDocumentDouble, PlatformOverridesvitest-auto-spy/angular/doubles
registerDirectiveMatchers, registerResourceMatchers, registerSignalMatchers, ResourceLike, SignalLikevitest-auto-spy/angular/matchers

Как /angular-http и /angular-router, три новые точки входа не реэкспортируют ядро. trackInjections остаётся в vitest-auto-spy/angular. npx vitest-auto-spy doctor сообщает о каждом перенесённом имени, которое ещё импортируется из /angular, как об ошибке helper-from-wrong-entry — в том числе в файлах, до которых не доходит ваш tsconfig. Подробности — в changelog к 5.21.0.

С 5.35.0 vitest-auto-spy/angular экспортирует и тип Spy<T>, так что import { type Spy, injectSpy } from 'vitest-auto-spy/angular' компилируется. Остальное ядро по-прежнему импортируется из vitest-auto-spy.

Исправления диагностики Angular и zone (5.6.0, 5.19.0) ​

Это исправления ошибок, но проверка, которая молча не работала, после обновления может начать сообщать о проблемах.

  • 5.6.0: проверяется каждый файл спеки. Под isolate: false enableAngularDiagnostics() и provideHttpTesting({ verifyOnTeardown }) регистрировали хуки один раз на модуль, поэтому проверялся только первый файл спеки в каждом воркере. Теперь проверяется каждый.
  • 5.6.0: незавершённые HTTP-запросы под sequence: { hooks: 'list' }. Проверка в конце теста читала пустой модуль и ничего не сообщала. А TestBed.inject на сброшенном TestBed пересобирал модуль, так что каждая спека с HTTP-тестированием падала со второго теста с ошибкой Cannot configure the test module when the test module has already been instantiated.
  • 5.19.0: диагностика видит и getTestBed(). ngModuleScopes, deadSchemas, shadowedProviders и проверка overrideComponentProvider видели только статический TestBed и пропускали getTestBed().configureTestingModule(…). TestBed.overrideTemplate теперь входит в измеряемое время TestBed.
  • 5.19.0: упавшая диагностика больше не пропускает сброс. Раньше падал следующий тест с Cannot configure the test module when the test module has already been instantiated — и называлась спека, в которой всё было в порядке.
  • 5.19.0: pendingRequests находит вложенный HttpTestingController. Чтение только первого уровня импортов не находило токен и молча выключало проверку.
  • 5.19.0: installProxyZonePatch() больше не наслаивается. Вызов из setup-файла под isolate: false оборачивал собственную обёртку: двести файлов оставляли двести слоёв Proxy на каждом it последнего файла. Зоны не вкладывались, так что результаты были верными, но каждый слой стоил времени. Повторная установка теперь ничего не делает, как и её откат.
  • 5.19.0: сторож ожидания эмиссии использует часы zone.js. Внутри fakeAsync вызов tick(1_500) при ожидании debounceTime(2_000) отклонял ожидание, которое сам же вот-вот должен был выполнить.

Изменения в поведении хелперов Angular (с 5.9.0 по 5.46.0) ​

Каждое из этих изменений заставляет спеку падать там, где раньше она молча проходила. После обновления ждите несколько красных спек; каждая скрывала настоящую проблему.

  • 5.9.0: stable() и setInputs() работают в проектах с zone.js. Они выполняют tick внутри TestBed.inject(NgZone). Под zoneless это NoopNgZone, так что там это ничего не стоит.
  • 5.9.0: runEffect() выполняет предыдущую очистку. Раньше каждый вызов оставлял ещё одну функцию очистки, и все они срабатывали разом при fixture.destroy(). Это ломало неидемпотентные очистки: queue.pop(), уменьшение счётчика, unsubscribe на общем subject.
  • 5.9.0: hasValue() у подмены ресурса следует правилу Angular (Angular v20 и новее): он сообщает, есть ли значение, а не какой сейчас статус. С defaultValue hasValue() равен true в состояниях loading, reloading и idle и false только при error или значении undefined. Шаблон вида @if (products.hasValue()) { … } @else { <spinner/> } показывает список, пока грузится следующая страница; старая подмена «по статусу» показывала спиннер.
  • 5.11.0: inputs в renderShallow отвергает имена, которых компонент не объявляет. Раньше Angular отвечал NG0303 в консоли и не задавал значение. Ключ, который молча ничего не делал, теперь падает — в спеке, которая раньше была зелёной.
  • 5.11.0: setInputs() на фикстуре без ɵcmp называет класс. Раньше он падал с Cannot read properties of undefined (reading 'inputs').
  • 5.19.0: setInputs() принимает входы, которые открывают host-директивы. Раньше он их отвергал и писал «It declares no inputs at all» про компонент, у которого входы есть.
  • 5.19.0: упавшая подмена ресурса бросает ошибку из value(), как Angular. Код компонента, который читает value() без проверки hasValue(), раньше проходил в спеке и падал в приложении. reload() теперь отвечает false, пока перезапускать нечего; раньше всегда был true.
  • 5.20.0: injectSpy(GenericClass) больше не выводит never. Для класса, конструктор которого принимает собственный параметр типа, 5.19.0 выводил never, и типизированный accessorSpies превращал это в ошибку присваивания. Теперь такой класс читается по ограничению параметра, например Spy<ModalRef<unknown>>. Нужен конкретный тип — укажите его явно: injectSpy<ModalRef<PurchaseOptions>>(ModalRef).
  • 5.46.0: toHaveSignalValue и toHaveResourceValue сравнивают содержимое Set и Map. Раньше любые два Set были равны, как и любые два Map. Проверка на неверное множество проходила.

Изменения в поведении спаев и проверок (с 5.2.0 по 5.44.0) ​

Эти исправления могут сделать зелёную спеку красной или сломать проверку типов. Каждая такая спека проходила по неверной причине.

  • 5.2.0: тип по умолчанию у параметра generic-класса доходит до спая. Для class FlagsConfigService<T = FlagsConfigDefaults> createSpyFromClass и injectSpy раньше выводили T как unknown. Тогда каждый член, типизированный через T, читался как unknown.
  • 5.2.0: restoreMocks: true сохраняет спаи аксессоров у подмены, созданной вне beforeEach. Раньше восстановление раннера запускалось до теста и возвращало настоящие аксессоры. Записи accessorSpies оставались, но их уже никто не читал.
  • 5.6.0: значение из overrides доходит до спая геттера. Для члена из gettersToSpyOn это значение раньше терялось, и геттер отвечал undefined. Теперь геттер его возвращает. Более поздний accessorSpies.getters.x.mockReturnValue(…) по-прежнему главнее.
  • 5.9.0: returns и selfReturning пропускают член, заданный в overrides. Это касается createAutoMock и provideAutoSpyForToken. Обычная функция, заданная там заранее, раньше приводила к TypeError: asVitestMock(...).mockImplementation is not a function из провайдера.
  • 5.15.0: геттер из overrides выполняется, только когда спека его читает. Раньше он выполнялся при сборке подмены. Геттер, который должен был бросить ошибку, падал ещё внутри TestBed.configureTestingModule.
  • 5.19.0: calledWith больше не считает разные аргументы одинаковыми. Две функции с одним именем, два Error, различающиеся только сообщением, или Set в другом порядке раньше получали один и тот же ответ. Спека, которая на это опиралась, теперь падает и печатает оба списка аргументов.
  • 5.19.0: resetAutoSpy сбрасывает и отложенные Once-значения, и настройку аксессоров. Значение из очереди mockReturnValueOnce раньше отвечало на первый вызов после сброса, уже в другом тесте. accessorSpies.getters.x.mockReturnValue(…) тоже переживал сброс. Спека, которая на это опиралась, теперь получает undefined.
  • 5.19.0: accessorSpies.getters и .setters типизированы по члену класса. Стаб геттера значением неверного типа теперь ошибка типов.
  • 5.19.0: хелперы ожидания эмиссий останавливаются на нужном значении. Спай в tap, finalize или defer после этого значения больше не вызывается. Спека, которая насчитывала пять вызовов tap, теперь видит один, как в продакшене. of(1).pipe(repeat()) больше не зависает.
  • 5.19.0: setupAutoSpy() закрывает ожидание эмиссии, которое тест не дождался через await. Раньше оно продолжало работать в следующем тесте и падало там. Теперь оно закрывается в конце своего теста, а предупреждение называет этот тест.
  • 5.44.0: функция в overrides для метода становится спаем этого метода. Это касается createSpyFromClass и provideAutoSpy. Раньше функция сохранялась как есть, поэтому expect(sanitizer.sanitize).toHaveBeenCalledOnce() компилировался, а потом бросал [Function sanitize] is not a spy.

Изменения в setup и заглушке сети (с 5.19.0 по 5.22.0) ​

Это хелперы из vitest-auto-spy/setup. Каждое изменение может поменять результат спеки, которая раньше проходила.

  • 5.19.0: advanceTimers() отказывается работать, когда подменены только часы.mockSystemTime() подменяет один Date, так что продвигать нечего. Раньше вызов проходил и ничего не делал. Теперь он бросает ошибку и называет setupFakeTimers().
  • 5.19.0: withSystemTime и mockSystemTime возвращают часы и под фейковыми таймерами набора тестов. Раньше откат там ничего не делал, и часы оставались там, где их бросил блок.
  • 5.20.0: blockNetwork больше не выключает MSW. Если server.listen() стоит в beforeAll, заглушка fetch раньше ложилась поверх MSW. Тогда каждый замоканный запрос fetch падал с fetch is stubbed in unit tests, включая запросы HttpClient.
  • 5.22.0: stubResponse({ body: null }) отправляет JSON-литерал null. Раньше null означал «без тела», и .json() бросал Unexpected end of JSON input. Чтобы тела не было, не указывайте body или напишите body: undefined. { body: null, status: 204 } (или 205, 304) теперь бросает ошибку с понятным текстом.

Изменения в пакете (5.19.0) ​

Они меняют то, как пакет ставится и загружается, а не то, что делает спай.

  • vitest — необязательная peer-зависимость. Проект на /bun или /node больше не ставит Vitest. Диапазон прежний: >=2.1.0.
  • vitest-auto-spy/package.json находится. Инструменты, которые читают манифест зависимости, например Storybook или Nx, раньше получали ERR_PACKAGE_PATH_NOT_EXPORTED.
  • require('vitest-auto-spy/eslint-plugin') типизирован правильно. Его CommonJS-декларация теперь export =. Раньше eslint.config.cts или .cjs проходил проверку типов на вызове, который падает, и отвергал рабочий.

Изменения в правилах ESLint (с 5.5.0 по 5.25.0) ​

После обновления ждите новых замечаний. Числа взяты из одного проекта на 1 759 файлов спек.

  • 5.5.0: два новых правила, оба warn. no-stub-class-double находит класс-заглушку с полями vi.fn(). no-structural-double находит объект из vi.fn() с типом вида { m: Mock }. Сначала они дали 12 замечаний в 8 файлах и 115 в 74. После изменения ниже — 10 в 7 и 5 в 4.
  • 5.5.0: prefer-provide-auto-spy видит больше подмен. Правило читает useClass:, useExisting:, useValue: new StubMock(), let, который заполняется в beforeEach, и TestBed.overrideProvider. Раньше overrideProvider не читало ни одно правило. В том проекте замечаний стало 154 вместо 18, на уровне error. Большинство этих подмен раньше отмечало no-structural-double на уровне warn.
  • 5.5.0: одна подмена — одно замечание. prefer-create-spy-from-class и no-structural-double молчат, когда объект уходит в DI Angular. Раньше объект в const мог получить два замечания: одно советовало createSpyFromClass, другое provideAutoSpy.
  • 5.5.0: no-overridden-provider видит провайдер, который заменяет TestBed.overrideProvider. Заменённый provideAutoSpy(X) — спай, которым никто не пользуется.
  • 5.24.0: no-redundant-mock-reset отмечает меньше сбросов. 5.23.0 отмечал ещё три случая, где сброс не лишний: восстановление или сброс в afterEach или afterAll, сброс после другого beforeEach и что угодно в beforeAll. Теперь правило их не отмечает.
  • 5.25.0: prefer-create-mock отмечает вложенные приведения фикстуры один раз. Раньше каждое приведение давало своё замечание. После исправления внешнего внутреннее приведение оставалось непроверенным.

Изменения в CLI (с 5.18.0 по 5.20.0) ​

Задача CI с perf --gate, doctor или init --check после обновления может сменить цвет.

  • 5.18.0: порог perf судит файл по его тестам, а не по размеру. Бюджет файла теперь считается в медианных тестах прогона. --factor применяется к каждому тесту; --max-file-ms — только нижняя граница. Старое правило отмечало большие файлы и медленные машины CI. Один и тот же файл был зелёным на ноутбуке и красным в CI.
  • 5.18.0: perf печатает то, что провалит --gate. Вместо двух таблиц «десять самых медленных» теперь файлы и тела тестов сверх бюджета. Старые таблицы показывали самые большие файлы, а они редко бывали медленными.
  • 5.19.0: неизвестный флаг — ошибка с кодом выхода 2. Раньше init --dryrun записывал файлы, а perf --gat проходил вообще без порога.
  • 5.19.0: init --check не реагирует на изменение одной только отметки версии. Новый релиз пакета больше не делает проверку красной, если инструкции не изменились.
  • 5.19.0: отчёты Code Quality сохраняют числа в обратных кавычках. returns 200 и returns 404 теперь две находки. Раньше виджет merge request показывал одну и терял другую.
  • 5.19.0: codemod правильно читает JSX. / самозакрывающегося тега принимался за начало регулярного выражения. Часть спеки .tsx тогда пропускали все преобразования и --verify.
  • 5.20.0: doctor предупреждает, когда обход останавливается на лимите. После 50 000 файлов он сообщает scan-cap-reached. Раньше он писал «No problems found.» про файлы, которые не читал.
  • 5.20.0: таблицы perf печатают путь последним и целиком. Раньше путь обрезался посередине, и скопировать его из лога CI было нельзя.

Общий strict: true начинает действовать (5.6.0) ​

До 5.6.0 setupAutoSpy({ strict: true }) ни на что не влиял: настройка попадала в копию модуля, которую не читала ни одна спека. 5.6.0 это исправил. Если ваш проект на 5.5 или раньше и эта опция задана, поведение меняется. Каждый ненастроенный вызов спая теперь бросает "… was called; this strict double has nothing configured for it". В одном проекте из-за этого покраснели десять тестов. Настройте вызов, который называет ошибка, или уберите strict для этого спая.

Что не изменилось ​

  • Ни один хелпер не удалён, не переименован и не помечен устаревшим. provideAutoSpy, injectSpy, renderShallow, createWithAutoSpies, stable, flushEffects, матчеры сигналов и ресурсов и хелперы mock*Prop работают как раньше.
  • В самой 5.0 ни один путь импорта в ваших спеках не меняется. (Позже, в 5.21.0, часть хелперов Angular переехала; см. выше.)
  • Angular остаётся необязательным. Проект на React, Vue, Svelte, NestJS или чистом Node не ставит ни одного Angular-пакета и не загружает их в свою программу TypeScript.
  • Peer-зависимость vitest по-прежнему начинается с 2.1. Одна установка работает с Vitest от 2.1 до 5.x.
  • Поддерживаются и zoneless, и zone.js. Для fakeAsync по-прежнему нужен vitest-auto-spy/zone.

Если вы идёте издалека ​

  • В Переходе на 4.0 есть настоящая работа: rxjs ушёл из публикуемых деклараций, а две группы хелперов переехали на свои пути импорта.
  • Переход на 3.0 — одна строка в package.json.
  • Переход на 2.0 важен, только если вы ещё на 1.x.

Опубликовано под лицензией MIT.