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

Совместимость ​

Эта страница объясняет, откуда взялись минимальные версии со страницы Установка. Она пригодится, когда обновление или закреплённая версия спорит с диапазонами ниже. Чтобы просто узнать, что ставить, хватит раздела Что нужно проекту.

ЗависимостьМинимумПочему, в одну строку
Vitest2.1типы спаев берут тип Mock из самого Vitest, а settledResults в нём появился в 2.x
rxjs7.2первая версия, где все нужные пакету операторы экспортируются из корневого пути rxjs
Angular20первая версия с provideZonelessChangeDetection и самая старая, которую Angular ещё поддерживает
Node.js22у 18 и 20 кончилась поддержка, а всем поддерживаемым версиям Vitest нужно больше
TypeScript4.7он нужен типизированным хелперам; чистый JavaScript работает без типов

Верхней границы нет ни у одного диапазона.

Vitest 2.1 ​

Типизированный spy.method.mock.settledResults берётся из типа Mock самого Vitest. В @vitest/spy поле settledResults появилось в 2.0, а устоялась линейка 2.x на 2.1.

Сами хелперы работают и на более старом Vitest, а на bun:test и node:test библиотека добавляет settledResults сама. Больше нужно только типам: на Vitest ниже 2.1 они уже не сходятся, поэтому диапазон начинается с 2.1.

vitest — необязательная peer-зависимость. Проекту, который импортирует только vitest-auto-spy/bun или vitest-auto-spy/node, ставить раннер Vitest не нужно. А проект, где Vitest есть, по-прежнему должен держать >=2.1.

Типы этих точек входа не упоминают vitest: /node, /bun, /bun-angular, /rstest, /jasmine-compat, /angular-router, /console, /nestjs, /observer-spy и /rxjs. Проект без Vitest проходит с ними проверку типов даже при skipLibCheck: false.

Spy<T> — один тип во всех точках входа, но спаи методов бывают двух видов:

  • Собственный MockInstance из Vitest — если любой файл, который проверяет tsconfig.json, импортирует точку входа, загружающую Vitest: vitest-auto-spy, /angular, /angular/doubles, /react, /vue, /svelte, /setup, /jasmine или /dom-stubs.
  • Тип мока без раннера — во всех остальных случаях. У него те же члены, кроме mockThrow из Vitest. Чтобы спай бросал ошибку на любом раннере, используйте failWith(error) из библиотеки.

Частая ошибка: набор тестов на Vitest, который импортирует только /nestjs, /observer-spy или /rxjs, получает тип без раннера: mockThrow нет, и спай не подходит под аннотацию MockInstance. Добавьте эту строку в любой файл, который включает tsconfig.json, например в спеку или .d.ts:

ts
import type {} from 'vitest-auto-spy';

rxjs 7.2 ​

Слой Observable собирает свои Subject из шести операторов: concatMap, delay, switchMap, take, takeUntil и takeWhile. Раньше они импортировались из rxjs/operators — старого глубокого пути, который rxjs 8 убирает. Теперь пакет берёт их из корневого пути rxjs. Там они появились в 7.2 — это сверено с объявлениями типов самого rxjs 7.2.0.

firstValueFrom есть с 7.0, с ним ничего не менялось. Angular с 16 по 22 принимает rxjs ^6.5.3 || ^7.4.0, поэтому у Angular-проекта на rxjs 7 уже стоит 7.4 или новее. Обновить rxjs до 7.2 или новее придётся только проекту на rxjs 6, 7.0 или 7.1.

Angular 20 ​

Если нет функции, которую пакет импортирует по имени, это ошибка связывания: не загружается вся точка входа, а не один хелпер. Поэтому минимум задают функции Angular, которые пакет импортирует по имени.

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

Angular 20 — ещё и самая старая версия, которую сам Angular поддерживает. У Angular 19 поддержка закончилась 2026-05-19, у 18, 17 и 16 — раньше. Так что минимум не отрезает ничего, что ещё получает исправления. Верхней границы нет намеренно: иначе на каждую мажорную версию Angular нужен был бы релиз, а тот, кто обновился первым, получал бы ERESOLVE.

@angular/platform-browser объявлен как peer-зависимость. Матчеры директив из vitest-auto-spy/angular/matchers импортируют из него By, а preload для Bun импортирует platformBrowserTesting и BrowserTestingModule. В плоском node_modules у npm пакет и так находился: он есть в каждом Angular-проекте. В изолированной раскладке pnpm он не находился вовсе, поэтому теперь объявлен. Оба этих импорта есть с Angular 16, так что минимум они не поднимают. В 20 изменилось только одно: путь @angular/platform-browser-dynamic/testing перестал быть рекомендуемым.

Сигнальным формам (vitest-auto-spy/signal-forms) нужен Angular 22: в нём @angular/forms их и выпускает. При этом @angular/forms объявлен с Angular 20 (>=20.0.0), как и @angular/core.

Необязательные peer-зависимости ​

Все peer-зависимости необязательные. Ставить нужно только те, что нужны импортированным точкам входа:

ПакетДиапазонКому нужен
vitest>=2.1.0корневой точке входа и всем, что работают на Vitest
rxjs>=7.2.0vitest-auto-spy/rxjs, /angular-router, /angular/doubles
@angular/core>=20.0.0всем точкам входа для Angular
@angular/common>=20.0.0vitest-auto-spy/angular-http, /angular-router
@angular/platform-browser>=20.0.0vitest-auto-spy/angular/matchers, /angular/doubles, /bun-angular
@angular/compiler>=20.0.0vitest-auto-spy/angular/matchers, /bun-angular
@angular/router>=20.0.0vitest-auto-spy/angular-router
@angular/forms>=20.0.0vitest-auto-spy/signal-forms, которой для работы нужен ещё и Angular 22
@rstest/core>=0.11.0vitest-auto-spy/rstest

Строгий установщик вроде pnpm отказывается разрешать импорт, который никто не объявил. Поэтому объявлен каждый пакет, который импортирует код. Необязательные они потому, что каждый нужен только точкам входа из своей строки. Точке входа, которой нет в таблице (например, /bun, /node или /nestjs), пакеты из этого списка не нужны.

У @angular/common есть своя причина. vitest-auto-spy/angular должна загружаться и в проекте, где есть @angular/core, но нет @angular/common. Статический импорт @angular/common внутри /angular сломал бы это всем, включая множество наборов тестов, которые вообще не проверяют HTTP. Поэтому хелперы HTTP вынесены в отдельную точку входа vitest-auto-spy/angular-http, и @angular/common импортируют только те точки входа, которым он нужен. rxjs держится вне ядра так же: хелперы для Observable лежат за vitest-auto-spy/rxjs.

Node.js 22 ​

У Node 18 и 20 кончилась поддержка, и всем поддерживаемым раннерам нужно больше:

  • Vitest 4 объявляет ^20.0.0 || ^22.0.0 || >=24.0.0. Vite 7, который он ставит, требует ^20.19.0 || >=22.12.0. На Node 18 прогон падает с TypeError: crypto.hash is not a function ещё до загрузки первой спеки.
  • Vitest 5 требует ^22.12.0 || ^24.0.0 || >=26.0.0. Ещё он объявляет @types/node как ^22.0.0 || >=24.0.0. Эта зависимость необязательная, но npm проверяет её, если она установлена. Поэтому проект на @types/node 20 получит ERESOLVE.

У билдера Angular CLI свой диапазон: @angular/build 22 требует Node ^22.22.3 || ^24.15.0 || >=26.0.0.

CI гоняет Node 22, 24 и 26. Опубликованный код собран под ES2022. На какой версии Node тесты идут быстрее, измерено на странице Производительность.

using на Node 22 ​

using spy = createSpyFromClass(UserService) сбрасывает спай, когда блок заканчивается. Метод [Symbol.dispose]() даёт библиотека, а синтаксис using — ваш инструментарий сборки.

  • Код после транспиляции работает на любой поддерживаемой Node. esbuild и tsc переписывают using в обычные вызовы, и именно так Vitest, Bun и Rstest запускают ваши спеки.
  • Код без транспиляции требует Node 24. Файл .js с using без транспиляции на Node 22 — это SyntaxError. Если ваша сборка не транспилирует код, вызывайте spy[Symbol.dispose]() или resetAutoSpy(spy) сами; resetAutoSpy берётся из той же точки входа, что и createSpyFromClass.
  • На Node 22 пакет сам добавляет Symbol.dispose. Речь о using после транспиляции, а не о синтаксисе. Переписанный using читает Symbol.dispose из глобального Symbol. В Node 24 он есть в каждом realm (realm — отдельный набор глобальных объектов). Node 22 добавляет его только в главный realm, как Symbol.for('nodejs.dispose'). Окружения jsdom и happy-dom в Vitest запускают спеки в отдельном realm vm, где его нет, и using бросил бы TypeError: Symbol.dispose is not defined.

Импорт пакета определяет Symbol.dispose в этом realm. Он берёт тот же символ из реестра, что и Node, поэтому ключ одинаков во всех realm процесса. Символ определяется только там, где его нет, неперечисляемым и настраиваемым (configurable); realm, где Symbol.dispose уже есть, остаётся как был.

ESM и CommonJS ​

Пакет выходит как ESM с типами .d.ts в комплекте. У двух точек входа есть ещё и сборка CommonJS:

  • vitest-auto-spy/node — для тестов node --test, написанных на CommonJS;
  • vitest-auto-spy/eslint-plugin — для CommonJS-конфига eslint.config.cjs.

Всё остальное — только ESM. require() точки входа поверх Vitest всё равно не мог бы сработать: Vitest не даёт себя подключить так и пишет Vitest cannot be imported in a CommonJS module using require(). Раннеры тестов загружают ESM сами, так что ничего не потеряно. Без этой бесполезной сборки опубликованный пакет стал примерно вдвое меньше. Текущий размер пакета и стоимость импорта — на странице Производительность.

В CommonJS-конфиге ESLint require — как раз правильный вызов:

js
// eslint.config.cjs
const autoSpy = require('vitest-auto-spy/eslint-plugin');

module.exports = [{ files: ['**/*.spec.ts'], ...autoSpy.configs.recommended }];

require('vitest-auto-spy/eslint-plugin') возвращает сам объект плагина, и типы .d.cts говорят ровно это (export =). Поэтому в eslint.config.cts запись autoSpy.default.rules — ошибка типов, а не код, который компилируется и падает при запуске.

TypeScript без rxjs ​

Ни один тип, который выпускает пакет, не ссылается на типы rxjs. Проект без rxjs не ставит его и не загружает его объявления в TypeScript. До 4.0.0 каждый import { createSpyFromClass } тянул за собой 189 файлов .d.ts из rxjs, и rxjs приходилось ставить даже для тестов без Observable. См. Переход на 4.0.

vitest-auto-spy/package.json ​

Манифест пакета есть в карте экспортов, поэтому vitest-auto-spy/package.json разрешается. Инструменты, которые читают package.json зависимости через резолвер Node (Storybook, Nx, помощник Renovate), получают файл, а не ERR_PACKAGE_PATH_NOT_EXPORTED.

Алиас во множественном числе vitest-auto-spies ​

vitest-auto-spies — тонкий пакет-алиас. Он реэкспортирует этот пакет, точку входа за точкой входа, так что опечатка ставит тот же код. Лучше пишите имя в единственном числе: алиас собирается из него и только повторяет его.

Как CI проверяет самые старые версии ​

Минимальные версии проверяются, а не только объявлены. Кроме обычной матрицы тестов, CI собирает tarball и ставит его в чистый проект с четырьмя парами раннера и rxjs:

  1. нижняя граница обоих: Vitest 2.1 с rxjs 7.2;
  2. Vitest 3 с rxjs 7.8;
  3. Vitest 4 с rxjs 7.8;
  4. текущие версии обоих.

В каждой паре запускается спека на опубликованной сборке и проверяются типы пробного файла на опубликованных объявлениях.

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