Skip to main content
Version: Next

Transformación de código

Jest ejecuta el código en su proyecto JavaScript, pero si utilizas alguna sintaxis no compatible con Node de forma nativa (como JSX, TypeScript, Vue template), entonces debes transformar ese código en JavaScript plano, similar a lo que harías al compilar para el Navegador.

Jest soporta esto gracias a la opción de configuración transform.

Un transformador es un módulo que proporciona un método para transformar archivos fuente. Por ejemplo, si quieres utilizar una nueva funcionalidad del idioma en sus módulos o pruebas que aún no son compatible con Node, podría utilizar un preprocesador de código que transpilaría una versión futura de JavaScript a una actual.

Jest almacenará en caché el resultado de una transformación e intentará invalidar ese resultado basado en una serie de factores, como el origen del archivo que se está transformando y cambiando la configuración.

Defaults

Jest ships with one transformer out of the box – babel-jest. Cargará la configuración de Babel de su proyecto y transformará cualquier archivo que coincida con la Expresión regular /\.[jt]sx?$/ (en otras palabras, cualquier archivo .js, .jsx, .ts o .tsx). In addition, babel-jest will inject the Babel plugin necessary for mock hoisting talked about in ES Module mocking.

tip

Recuerde incluir explícitamente el transformador predeterminado babel-jest, si desea usarlo junto con preprocesadores de código adicionales:

"transform": {
"\\.[jt]sx?$": "babel-jest",
"\\.css$": "some-css-transformer",
}

Escribiendo transformadores personalizados

You can write your own transformer. The API of a transformer is as follows:

interface TransformOptions<TransformerConfig = unknown> {
supportsDynamicImport: boolean;
supportsExportNamespaceFrom: boolean;
/**
* The value is:
* - `false` if Jest runs without Node ESM flag `--experimental-vm-modules`
* - `true` if the file extension is defined in [extensionsToTreatAsEsm](Configuration.md#extensionstotreatasesm-arraystring)
* and Jest runs with Node ESM flag `--experimental-vm-modules`
*
* See more at https://jestjs.io/docs/next/ecmascript-modules
*/
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;
/** Versión Stringified del `config` - útil en el busting de caché. */
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>;
};
note

Las definiciones anteriores fueron recortadas por brevedad. Full code can be found in Jest repo on GitHub (remember to choose the right tag/commit for your version of Jest).

Hay un par de maneras en que puedes importar código en Jest - usando Common JS (require) o los Módulos ECMAScript (import - que existe en versiones estáticas y dinámicas). Jest pasa archivos a través de transformación de código bajo demanda (por ejemplo, cuando se evalúa una necesidad o importación). Este proceso, también conocido como "transpilación", podría suceder sincrónicamente (en el caso de requerir), o de forma asincrónica (en el caso de importar o importar(), el último de los cuales también funciona desde módulos Common JS). Por esta razón, la interfaz expone ambos pares de métodos para procesos asincrónicos y sincrónicos: proceso{Async} y getCacheKey{Async}. Este último es llamado para averiguar si necesitamos llamar al proceso{Async} en absoluto.

Asynchronous transpilation can fall back to the synchronous process call if processAsync is unimplemented, but synchronous transpilation cannot use the asynchronous processAsync call. If your codebase is ESM only, implementing the async variants are sufficient. Otherwise, if any code is loaded through require (including createRequire from within ESM), then you need to implement the synchronous process variant.

Be aware that node_modules is not transpiled with default config, the transformIgnorePatterns setting must be modified in order to do so.

Semi-related to this are the supports flags we pass (see CallerTransformOptions above), but those should be used within the transform to figure out if it should return ESM or CJS, and has no direct bearing on sync vs async

Though not required, we highly recommend implementing getCacheKey as well, so we do not waste resources transpiling when we could have read its previous result from disk. You can use @jest/create-cache-key-function to help implement it.

Instead of having your custom transformer implement the Transformer interface directly, you can choose to export createTransformer, a factory function to dynamically create transformers. This is to allow having a transformer config in your jest config.

note

ECMAScript module support is indicated by the passed in supports* options. Specifically supportsDynamicImport: true means the transformer can return import() expressions, which is supported by both ESM and CJS. If supportsStaticESM: true it means top level import statements are supported and the code will be interpreted as ESM and not CJS. See Node's docs for details on the differences.

tip

Make sure process{Async} method returns source map alongside with transformed code, so it is possible to report line information accurately in code coverage and test errors. Inline source maps also work but are slower.

During the development of a transformer it can be useful to run Jest with --no-cache to frequently delete cache.

Ejemplos

TypeScript with type checking

While babel-jest by default will transpile TypeScript files, Babel will not verify the types. If you want that you can use ts-jest.

Transforming images to their path

Importing images is a way to include them in your browser bundle, but they are not valid JavaScript. One way of handling it in Jest is to replace the imported value with its filename.

fileTransformer.js
const path = require('path');

module.exports = {
process(sourceText, sourcePath, options) {
return {
code: `module.exports = ${JSON.stringify(path.basename(sourcePath))};`,
};
},
};
jest.config.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',
},
};