Расширение репортеров
Вы можете импортировать репортеры из vitest/reporters
и расширять их для создания собственных репортеров.
Расширение встроенных репортеров
В целом, вам не нужно создавать свой репортер с нуля. vitest
поставляется с несколькими встроенными репортерами по умолчанию, которые вы можете расширить.
import { DefaultReporter } from 'vitest/reporters';
export default class MyDefaultReporter extends DefaultReporter {
// здесь ваш код
}
Конечно, вы можете создать свой репортер с самого начала. Просто расширьте класс BaseReporter
и реализуйте необходимые методы.
А вот пример пользовательского репортера:
// ./custom-reporter.js
import { BaseReporter } from 'vitest/reporters';
export default class CustomReporter extends BaseReporter {
onCollected() {
const files = this.ctx.state.getFiles(this.watchFilters);
this.reportTestSummary(files);
}
}
Или реализуйте интерфейс Reporter
:
// ./custom-reporter.js
import { Reporter } from 'vitest/reporters';
export default class CustomReporter implements Reporter {
onCollected() {
// вывести что-нибудь
}
}
Затем вы можете использовать свой пользовательский репортер в файле vitest.config.ts
:
import { defineConfig } from 'vitest/config';
import CustomReporter from './custom-reporter.js';
export default defineConfig({
test: {
reporters: [new CustomReporter()],
},
});
Отчетные задачи
WARNING
Это экспериментальный API. Критические изменения могут не соответствовать SemVer. Пожалуйста, зафиксируйте версию Vitest при использовании этого API.
Вы можете получить доступ к этому API, вызвав vitest.state.getReportedEntity(runnerTask)
:
import type { Vitest } from 'vitest/node';
import type { RunnerTestFile } from 'vitest';
import type { Reporter, TestModule } from 'vitest/reporters';
class MyReporter implements Reporter {
ctx!: Vitest;
onInit(ctx: Vitest) {
this.ctx = ctx;
}
onFinished(files: RunnerTestFile[]) {
for (const fileTask of files) {
// обратите внимание на то, что в старой реализации задачи используется "file" вместо "module"
const testModule = this.ctx.state.getReportedEntity(
fileTask
) as TestModule;
for (const task of testModule.children) {
// ^?
console.log('finished', task.type, task.fullName);
}
}
}
}
Мы планируем стабилизировать этот API в Vitest 2.1.
TestCase
TestCase
представляет собой отдельный тест.
declare class TestCase {
readonly type = 'test' | 'custom';
/**
* Экземпляр задачи.
* @experimental Публичный API задач является экспериментальным и не следует SemVer.
*/
readonly task: RunnerTestCase | RunnerCustomCase;
/**
* Проект, связанный с тестом.
*/
readonly project: TestProject;
/**
* Прямая ссылка на тестовый модуль, где определен тест.
*/
readonly module: TestModule;
/**
* Имя теста.
*/
readonly name: string;
/**
* Полное имя теста, включая все родительские наборы, разделенные символом `>`.
*/
readonly fullName: string;
/**
* Уникальный идентификатор.
* Этот идентификатор детерминирован и будет одинаковым для одного и того же теста при многократных запусках.
* Идентификатор основан на имени проекта, идентификаторе модуля и позиции теста.
*/
readonly id: string;
/**
* Местоположение в модуле, где был определен тест.
* Местоположения собираются только если в конфигурации включен параметр `includeTaskLocation`.
*/
readonly location: { line: number; column: number } | undefined;
/**
* Родительский набор. Если тест был вызван непосредственно внутри модуля, родителем будет сам модуль.
*/
readonly parent: TestSuite | TestModule;
/**
* Опции, с которыми был инициирован тест.
*/
readonly options: TaskOptions;
/**
* Проверяет, прошел ли тест набор.
* Если тест еще не завершен или был пропущен, вернет `true`.
*/
ok(): boolean;
/**
* Метаданные, прикрепленные пользователем, прикрепленные к тесту во время его выполнения.
*/
meta(): TaskMeta;
/**
* Результаты теста. Будет `undefined`, если тест еще не завершен или только что был собран.
*/
result(): TestResult | undefined;
/**
* Полезная информация о тесте, такая как продолжительность, использование памяти и т. д.
*/
diagnostic(): TestDiagnostic | undefined;
}
export type TestResult =
| TestResultPassed
| TestResultFailed
| TestResultSkipped;
export interface TestResultPassed {
/**
* Тест успешно пройден.
*/
state: 'passed';
/**
* Ошибки, которые были выброшены во время выполнения теста.
*
* **Примечание:** Если тест был успешно повторен, ошибки все равно будут сообщены.
*/
errors: TestError[] | undefined;
}
export interface TestResultFailed {
/**
* Тест не удалось выполнить.
*/
state: 'failed';
/**
* Ошибки, которые были выброшены во время выполнения теста.
*/
errors: TestError[];
}
export interface TestResultSkipped {
/**
* Тест был пропущен с флагом `only`, `skip` или `todo`.
* Вы можете увидеть, какой из них был использован, в опции `mode`.
*/
state: 'skipped';
/**
* Пропущенные тесты не содержат ошибок.
*/
errors: undefined;
}
export interface TestDiagnostic {
/**
* Если продолжительность теста превышает `slowTestThreshold`.
*/
slow: boolean;
/**
* Объем памяти, использованный тестом, в байтах.
* Это значение доступно только если тест был выполнен с флагом `logHeapUsage`.
*/
heap: number | undefined;
/**
* Время выполнения теста в мс.
*/
duration: number;
/**
* Время в мс, когда тест начался.
*/
startTime: number;
/**
* Количество раз, когда тест был повторен.
*/
retryCount: number;
/**
* Количество раз, когда тест был повторен, как настроено опцией `repeats`.
* Это значение может быть меньше, если тест провалился во время повтора и не настроен `retry`.
*/
repeatCount: number;
/**
* Если тест был нестабильным (прошел после повторного запуска).
*/
flaky: boolean;
}
TestSuite
TestSuite
представляет собой отдельный набор, который содержит тесты и другие наборы.
declare class TestSuite {
readonly type = 'suite';
/**
* Экземпляр задачи.
* @experimental Публичный API задач является экспериментальным и не следует SemVer.
*/
readonly task: RunnerTestSuite;
/**
* Проект, связанный с тестом.
*/
readonly project: TestProject;
/**
* Прямая ссылка на тестовый модуль, где определен набор.
*/
readonly module: TestModule;
/**
* Имя набора.
*/
readonly name: string;
/**
* Полное имя набора, включая все родительские наборы, разделенные символом `>`.
*/
readonly fullName: string;
/**
* Уникальный идентификатор.
* Этот идентификатор детерминирован и будет одинаковым для одного и того же теста при многократных запусках.
* Идентификатор основан на имени проекта, идентификаторе модуля и позиции теста.
*/
readonly id: string;
/**
* Местоположение в модуле, где был определен набор.
* Местоположения собираются только если в конфигурации включен параметр `includeTaskLocation`.
*/
readonly location: { line: number; column: number } | undefined;
/**
* Коллекция наборов и тестов, которые являются частью этого набора.
*/
readonly children: TaskCollection;
/**
* Опции, с которыми был инициирован набор.
*/
readonly options: TaskOptions;
}
TestModule
TestModule
представляет собой отдельный файл, который содержит наборы и тесты.
declare class TestModule extends SuiteImplementation {
readonly type = 'module';
/**
* Экземпляр задачи.
* @experimental Публичный API задач является экспериментальным и не следует SemVer.
*/
readonly task: RunnerTestFile;
/**
* Коллекция наборов и тестов, которые являются частью этого модуля.
*/
readonly children: TestCollection;
/**
* Обычно это абсолютный путь к файлу в формате Unix.
* Это может быть виртуальный идентификатор, если файл не находится на диске.
* Это значение соответствует идентификатору `ModuleGraph` из Vite.
*/
readonly moduleId: string;
/**
* Полезная информация о модуле, такая как продолжительность, использование памяти и т. д.
* Если модуль еще не был выполнен, все диагностические значения будут равны `0`.
*/
diagnostic(): ModuleDiagnostic;
}
export interface ModuleDiagnostic {
/**
* Время на импорт и инициализацию окружения.
*/
environmentSetupDuration: number;
/**
* Время, необходимое Vitest для настройки тестового окружения (раннер, моки и т. д.).
*/
prepareDuration: number;
/**
* Время, необходимое для импорта тестового модуля.
* Это включает импорт всего в модуле и выполнение колбэков набора.
*/
collectDuration: number;
/**
* Время, необходимое для импорта модуля настройки.
*/
setupDuration: number;
/**
* Накопленная продолжительность всех тестов и хуков в модуле.
*/
duration: number;
}
TestCollection
TestCollection
представляет собой коллекцию наборов и тестов. Он также предоставляет полезные методы для итерации по себе.
declare class TestCollection {
/**
* Возвращает тест или набор по определенному индексу в массиве.
*/
at(index: number): TestCase | TestSuite | undefined;
/**
* Количество тестов и наборов в коллекции.
*/
size: number;
/**
* Возвращает коллекцию в виде массива для удобной работы.
*/
array(): (TestCase | TestSuite)[];
/**
* Фильтрует все наборы, которые являются частью этой коллекции и ее дочерних элементов.
*/
allSuites(): IterableIterator<TestSuite>;
/**
* Фильтрует все тесты, которые являются частью этой коллекции и ее дочерних элементов.
*/
allTests(state?: TestResult['state'] | 'running'): IterableIterator<TestCase>;
/**
* Фильтрует только тесты, которые являются частью этой коллекции.
*/
tests(state?: TestResult['state'] | 'running'): IterableIterator<TestCase>;
/**
* Фильтрует только наборы, которые являются частью этой коллекции.
*/
suites(): IterableIterator<TestSuite>;
[Symbol.iterator](): IterableIterator<TestSuite | TestCase>;
}
Например, вы можете перебрать все тесты внутри модуля, вызвав testModule.children.allTests()
:
function onFileCollected(testModule: TestModule): void {
console.log('collecting tests in', testModule.moduleId);
// перебрать все тесты и наборы в модуле
for (const task of testModule.children.allTests()) {
console.log('collected', task.type, task.fullName);
}
}
TestProject
TestProject
— это проект, связанный с модулем. Каждый тест и набор внутри этого модуля будет ссылаться на один и тот же проект.
Проект полезен для получения конфигурации или предоставленного контекста.
declare class TestProject {
/**
* Глобальный экземпляр vitest.
* @experimental Публичный API Vitest является экспериментальным и не следует SemVer.
*/
readonly vitest: Vitest;
/**
* Проект в рабочем пространстве, с которым связан этот тестовый проект.
* @experimental Публичный API Vitest является экспериментальным и не следует SemVer.
*/
readonly workspaceProject: WorkspaceProject;
/**
* Экземпляр dev-сервера Vite. Каждый проект рабочей области имеет свой собственный сервер.
*/
readonly vite: ViteDevServer;
/**
* Разрешенная конфигурация проекта.
*/
readonly config: ResolvedProjectConfig;
/**
* Разрешенная глобальная конфигурация. Если нет проектов рабочей области, она будет такой же, как `config`.
*/
readonly globalConfig: ResolvedConfig;
/**
* Сериализованная конфигурация проекта. Это конфигурация, которую получают тесты.
*/
get serializedConfig(): SerializedConfig;
/**
* Имя проекта или пустая строка, если не задано.
*/
name(): string;
/**
* Пользовательский контекст, предоставленный проекту.
*/
context(): ProvidedContext;
/**
* Предоставить пользовательский сериализуемый контекст проекту. Этот контекст будет доступен для тестов после их запуска.
*/
provide<T extends keyof ProvidedContext & string>(
key: T,
value: ProvidedContext[T]
): void;
}
Экспортируемые репортеры
vitest
поставляется с несколькими встроенными репортерами, которые вы можете использовать сразу.
Встроенные репортеры:
BasicReporter
DefaultReporter
DotReporter
JsonReporter
VerboseReporter
TapReporter
JUnitReporter
TapFlatReporter
HangingProcessReporter
Базовые абстрактные репортеры:
BaseReporter
Репортеры-интерфейсы:
Reporter