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

Диагностика в редакторе ​

Правила линтера из библиотеки могут подчёркивать ошибки в редакторе, пока вы пишете, а не в CI час спустя. Они ловят конструкции, которые проходят, но ничего не проверяют: expect() внутри subscribe(), колбэк done, который Vitest никогда не вызовет, спай с типом класса.

Отдельный плагин для редактора не нужен. Правила входят в пакет как vitest-auto-spy/eslint-plugin, а каждый редактор ниже уже умеет запускать ESLint. Одни и те же правила работают в редакторе и в CI, поэтому то, что прошло локально, не упадёт на сборке.

Настройка один раз:

bash
npm i -D vitest-auto-spy eslint typescript-eslint
js
// eslint.config.js — flat config, в корне репозитория
import tseslint from 'typescript-eslint';
import autoSpy from 'vitest-auto-spy/eslint-plugin';

export default [
  ...tseslint.configs.recommended,
  {
    files: ['**/*.spec.ts', '**/*.spec.tsx', '**/*.test.ts', '**/*.test.tsx'],
    ...autoSpy.configs.recommended,
  },
];

typescript-eslint нужен, чтобы ESLint читал файлы .ts. Если конфиг ESLint у вас уже есть, добавьте только блок для файлов спек, после остальных блоков. autoSpy.configs.recommended — один объект с plugins и rules, без files, поэтому где работают правила, решает список files рядом с ним. Другие варианты — ESLint-плагин.

Затем включите ESLint в своём редакторе, как описано ниже.

WebStorm и другие IDE от JetBrains ​

WebStorm, IntelliJ IDEA Ultimate, PhpStorm, PyCharm Professional и RubyMine запускают ESLint сами. Поэтому правила видны прямо в коде, в окне Problems и в Code → Inspect Code, без установки плагина.

В Settings → Languages & Frameworks → JavaScript → Code Quality Tools → ESLint выберите Automatic ESLint configuration. WebStorm сам найдёт eslint.config.js и локальный eslint. Manual нужен, только если конфиг лежит вне корня проекта; тогда задайте ESLint package (node_modules/eslint) и Configuration file.

Три вещи, которые выглядят как «правила не работают»:

  • Используйте flat config (eslint.config.js). Старая форма .eslintrc с plugins: ['vitest-auto-spy'] ищет пакет с именем eslint-plugin-*, а этот плагин — подпуть пакета vitest-auto-spy. WebStorm поддерживает flat config с 2023.3; на более старой версии обновите IDE.
  • Ограничьте блок файлами спек, как в списке files выше. Object.defineProperty или объект из vi.fn() в коде приложения — это нормально; все правила здесь про тестовый код.
  • Быстрые исправления идут от ESLint. ⌥⏎ на подсвеченной строке предлагает ESLint: Fix current file и, если у правила есть подсказка, это конкретное исправление. Правила с безопасной заменой исправляют сами; остальные только предлагают, потому что замена меняет поведение и её нужно прочитать.

Во время миграции Code → Inspect Code… с областью «тестовые исходники» показывает все находки, сгруппированные по правилам.

Отдельного плагина для JetBrains нет

Плагин в Marketplace повторял бы то, что IDE и так делает через ESLint, и требовал бы второй копии каждого правила. Если в репозитории вообще нет ESLint, добавьте eslint.config.js, показанный выше.

VS Code, Cursor, Windsurf, VSCodium ​

Установите расширение ESLint. С конфигом выше правила видны прямо в коде и в панели Problems.

jsonc
// .vscode/settings.json
{
  // При сохранении применяются правила, которые исправляют сами ("explicit" — при сохранении
  // вручную, не при автосохранении). Остальные предлагают замену по ⌘. как подсказку.
  "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" },

  // Нужна только на ESLint 8; на ESLint 9 и новее flat config и так по умолчанию, строка не мешает.
  "eslint.useFlatConfig": true,
}

Cursor, Windsurf и VSCodium ставят то же расширение ESLint из Open VSX и читают тот же .vscode/settings.json.

Что именно подчёркивается ​

ФормаПочему это ошибкаПравило
expect() внутри subscribe()молчащий поток не вызывает колбэк, и тест проходит, ничего не проверивno-expect-in-subscribe
it('x', (done) => …) и done.fail(…)Vitest передаёт TestContext, а не done; тест проходит, почти не выполнив телоno-done-callback
цепочка .then() с проверкой, которую никто не ждётпроверка выполняется после конца теста, где ничего не падаетno-floating-assertion
{ provide: X, useValue: { m: vi.fn() } }provideAutoSpy(X) сам подхватывает новый метод классаprefer-provide-auto-spy
объект из vi.fn() вместо классаcreateSpyFromClass(X) читает класс, а не список, который устареваетprefer-create-spy-from-class
TestBed.inject<X>() или приведение типа после негоinjectSpy(X) возвращает Spy<X> без дженерика и приведенияprefer-inject-spy
vi.mocked() поверх того, что уже спайMocked<T> теряет calledWith, resolveWith и nextWith; исправляется автоматическиno-mocked-for-spy
TestBed.inject(X) as Spy<X>с этой библиотекой такое приведение не компилируется; asSpy(…) говорит то же без приведения и исправляется автоматическиprefer-as-spy
Object.defineProperty в спекеникто не записывает откат; mockReadonlyProp / mockValueProp записываютno-object-define-property
экспортированный объект из vi.fn() на уровне модуляпри isolate: false все файлы спек делят один набор спаевno-shared-module-level-mock
один и тот же токен дважды в одном массиве провайдероввторой провайдер молча заменяет первыйno-overridden-provider
TestBed.inject() раньше override* в том же наборе тестовinject создаёт модуль, и каждый следующий override бросает ошибкуno-inject-before-override
spyOn(o, 'm'), jasmine.*, fail(, .withContext(spyOn из Jasmine подменяет метод, а vi.spyOn вызывает настоящий, так что переименование молча меняет поведение; остальное — ReferenceErrorno-jasmine-globals
.and / .calls / .withArgs, которые никто не установилони берутся из vitest-auto-spy/jasmine; без него строка читает undefinedjasmine-namespace-without-entry
spy.calls.saveArgumentsByValue()здесь ничего не делает, и спека начинает проверять состояние после измененияno-save-arguments-by-value

Последние три — для набора тестов, который переезжает с jasmine-auto-spies.

Частая ошибка при такой миграции: держать prefer-native-spy-api (правило, которое заменяет вызовы в стиле Jasmine на родные вызовы Vitest) включённым с первого дня. Поначалу оно подчёркивает каждую строку переходного кода. Выключите его ('off') на время и включите для последнего шага: миграция закончена, когда оно молчит.

Каждое сообщение называет найденное и одно исправление, а в конце даёт Docs: и ссылку на раздел правила в Правилах ESLint. Полные описания и уровни — в ESLint-плагине.

Ошибки, которых ESLint не видит, сообщаются при запуске теста: импорт не той точки входа для вашего раннера или вызов nextWith без import 'vitest-auto-spy/rxjs'. Оба бросают ошибку с сообщением, где названо исправление и дана ссылка на страницу; см. Ошибки, которые сами называют исправление.

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