Преобразование кода
Jest выполняет код в вашем проекте как JavaScript, но если вы используете синтаксис, не поддерживаемый Node по умолчанию (такой как JSX, TypeScript, шаблоны Vue), то вам нужно преобразовать этот код в обычный JavaScript, аналогично тому, как вы это делаете при сборке для браузеров.
Jest поддерживает это через опцию конфигурации transform.
Преобразователь — это модуль, который предоставляет метод для преобразования исходных файлов. Например, если вы хотите использовать новую языковую функцию в своих модулях или тестах, которая ещё не поддерживается Node, вы можете подключить препроцессор кода, который бы транспилировал будущую версию JavaScript в текущую.
Jest кэширует результат преобразования и пытается аннулировать этот результат на основе нескольких факторов, таких как исходный файл, который преобразуется, и изменение конфигурации.
Значения по умолчанию
Jest поставляется с одним встроенным преобразователем — babel-jest. Он загрузит конфигурацию Babel вашего проекта и преобразует любой файл, соответствующий /\.[jt]sx?$/ RegExp (другими словами, любой .js, .jsx, .ts или .tsx файл). Кроме того, babel-jest внедрит плагин Babel, необходимый для подъема моков, о котором говорилось в мокировании ES-модулей.
Помните, что необходимо явно включить преобразователь по умолчанию babel-jest, если вы хотите использовать его вместе с дополнительными препроцессорами кода:
"transform": {
"\\.[jt]sx?$": "babel-jest",
"\\.css$": "some-css-transformer",
}
Написание пользовательских преобразователей
Вы можете написать собственный преобразователь. API преобразователя следующий:
interface TransformOptions<TransformerConfig = unknown> {
supportsDynamicImport: boolean;
supportsExportNamespaceFrom: boolean;
supportsStaticESM: boolean;
supportsTopLevelAwait: boolean;
instrument: boolean;
/** Cached file system which is used by `jest-runtime` to improve performance. */
cacheFS: Map<string, string>;
/** Jest configuration of currently running project. */
config: ProjectConfig;
/** Stringified version of the `config` - useful in cache busting. */
configString: string;
/** Transformer configuration passed through `transform` option by the user. */
transformerConfig: TransformerConfig;
}
type TransformedSource = {
code: string;
map?: RawSourceMap | string | null;
};
interface SyncTransformer<TransformerConfig = unknown> {
canInstrument?: boolean;
getCacheKey?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => string;
getCacheKeyAsync?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => Promise<string>;
process: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => TransformedSource;
processAsync?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => Promise<TransformedSource>;
}
interface AsyncTransformer<TransformerConfig = unknown> {
canInstrument?: boolean;
getCacheKey?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => string;
getCacheKeyAsync?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => Promise<string>;
process?: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => TransformedSource;
processAsync: (
sourceText: string,
sourcePath: string,
options: TransformOptions<TransformerConfig>,
) => Promise<TransformedSource>;
}
type Transformer<TransformerConfig = unknown> =
| SyncTransformer<TransformerConfig>
| AsyncTransformer<TransformerConfig>;
type TransformerCreator<
X extends Transformer<TransformerConfig>,
TransformerConfig = unknown,
> = (transformerConfig?: TransformerConfig) => X;
type TransformerFactory<X extends Transformer> = {
createTransformer: TransformerCreator<X>;
};
Вышеприведённые определения были сокращены для краткости. Полный код можно найти в репозитории Jest на GitHub (не забудьте выбрать правильную метку/коммит для вашей версии Jest).
Существует несколько способов импорта кода в Jest — с использованием Common JS (require) или ECMAScript Modules (import — которые существуют в статических и динамических версиях). Jest проходит файлы через преобразование кода по требованию (например, когда оценивается require или import). Этот процесс, также известный как «транспиляция», может происходить *синхронно* (в случае require) или *асинхронно* (в случае import или import(), последнее из которых также работает с Common JS-модулей). По этой причине интерфейс предоставляет обе пары методов для асинхронных и синхронных процессов: process{Async} и getCacheKey{Async}. Последний вызывается, чтобы определить, нужно ли вообще вызывать process{Async}. Поскольку асинхронное преобразование может происходить синхронно без проблем, асинхронный случай может «вернуться» к синхронному варианту, но не наоборот.
Таким образом, если ваша кодовая база — только ESM, то достаточно реализовать асинхронные варианты. В противном случае, если какой-либо код загружается через require (включая createRequire изнутри ESM), то необходимо реализовать синхронный вариант. Имейте в виду, что node_modules не транспилируется с использованием конфигурации по умолчанию.
Непосредственно связанными с этим являются флаги поддержки, которые мы передаём (см. CallerTransformOptions выше), но их следует использовать внутри преобразования, чтобы определить, должен ли он вернуть ESM или CJS, и они не влияют напрямую на синхронность/асинхронность.
Хотя это не обязательно, мы *настоятельно рекомендуем* реализовать также getCacheKey, чтобы не тратить ресурсы на транспиляцию, когда мы могли бы прочитать её предыдущий результат с диска. Вы можете использовать @jest/create-cache-key-function, чтобы помочь в его реализации.
Вместо того, чтобы ваш пользовательский преобразователь реализовывал интерфейс Transformer напрямую, вы можете выбрать экспорт createTransformer, функцию-фабрику для динамического создания преобразователей. Это позволяет иметь конфигурацию преобразователя в вашей конфигурации Jest.
Обратите внимание, что поддержка ECMAScript-модулей указывается переданными в supports* опциями. В частности, supportsDynamicImport: true означает, что преобразователь может возвращать выражения import(), что поддерживается как ESM, так и CJS. Если supportsStaticESM: true, это означает, что операторы import верхнего уровня поддерживаются, и код будет интерпретироваться как ESM, а не CJS. Подробности об отличиях см. в документации Node .
Убедитесь, что метод process{Async} возвращает карту исходных позиций вместе с преобразованным кодом, чтобы было возможно точно сообщать информацию о строках в покрытии кода и ошибках тестов. Встроенные карты исходных позиций также работают, но медленнее.
Во время разработки преобразователя может быть полезно запустить Jest с --no-cache, чтобы часто очистить кэш.
Примеры
TypeScript с проверкой типов
Хотя babel-jest по умолчанию транспилирует файлы TypeScript, Babel не будет проверять типы. Если вам это нужно, можно использовать ts-jest.
Преобразование изображений в их путь
Импорт изображений — способ включить их в ваш браузерный бандл, но они не являются допустимым JavaScript. Один из способов обработки этого в Jest — заменить импортированное значение именем файла.
const path = require('path');
module.exports = {
process(sourceText, sourcePath, options) {
return {
code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`,
};
},
};
module.exports = {
transform: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/fileTransformer.js',
},
};
© 2022 Facebook, Inc.
Licensed under the MIT License.
https://jestjs.io/docs/code-transformation