Spec-Zone.ru › Jest

Преобразование кода

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))};`,
    };
  },
};
fileTransformer.js
module.exports = {
  transform: {
    '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
      '<rootDir>/fileTransformer.js',
  },
};
jest.config.js

© 2022 Facebook, Inc.
Licensed under the MIT License.
https://jestjs.io/docs/code-transformation

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API