Transformation de code
Jest exécute le code de votre projet en tant que JavaScript, mais si vous utilisez une syntaxe qui n'est pas prise en charge par Node (comme JSX, TypeScript, les templates Vue), vous devrez transformer ce code en JavaScript ordinaire, comme vous le feriez lors de la création pour les navigateurs.
Jest prend en charge ceci via l'option de configuration transform
.
Un transformateur est un module qui fournit une méthode pour transformer les fichiers source. Par exemple, si vous souhaitez pouvoir utiliser une nouvelle fonctionnalité du langage dans vos modules ou tests qui n'est pas encore prise en charge par Node, vous pourriez intégrer un préprocesseur de code qui transposerait une future version de JavaScript dans une version actuelle.
Jest mettra en cache le résultat d'une transformation et tentera d'invalider ce résultat en fonction d'un certain nombre de facteurs, comme la source du fichier transformé et la modification de la configuration.
Defaults
Jest ships with one transformer out of the box – babel-jest
. Il chargera la configuration Babel de votre projet et transformera tout fichier correspondant à la RegExp suivante /\.[jt]sx?$/
(signifiant tout fichier .js
, .jsx
, .ts
ou .tsx
). En outre, babel-jest
injectera le plugin Babel nécessaire pour le montage de simulation dont on parle dans Simulation module ES.
N'oubliez pas d'inclure explicitement le transformateur par défaut babel-jest
, si vous souhaitez l'utiliser en parallèle avec des préprocesseurs de code supplémentaires :
"transform": {
"\\.[jt]sx?$": "babel-jest",
"\\.css$": "some-css-transformer",
}
Écriture de transformateurs personnalisés
Vous pouvez écrire votre propre transformateur. L'API d'un transformateur est la suivante :
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>;
/** Configuration de Jest du projet en cours d'exécution. */
config: ProjectConfig;
/** Version stringifiée de `config` - utile pour la suppression du cache. */
configString: string;
/** Configuration du transformateur passée par l'utilisateur via l'option `transform`. */
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>;
};
Les définitions ci-dessus ont été réduites par souci de concision. Full code can be found in Jest repo on GitHub (remember to choose the right tag/commit for your version of Jest).
Il y a deux façons d'importer du code dans Jest - en utilisant Common JS (require
) ou des Modules ECMAScript (import
- qui existe dans les versions statiques et dynamiques). Jest passe les fichiers à travers la transformation de code à la demande (par exemple quand un require
ou un import
est évalué). Ce processus, également appelé « transpilation », peut se dérouler de manière synchrone (dans le cas de require
), ou asynchrone (dans le cas de import
ou import()
, ce dernier fonctionnant également à partir de modules Common JS). Pour cette raison, l'interface expose les deux paires de méthodes pour des processus asynchrones et synchrones : process{Async}
et getCacheKey{Async}
. Ce dernier est appelé pour savoir si nous devons appeler process{Async}
.
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. Vous pouvez utiliser @jest/create-cache-key-function
pour vous aider à l'implémenter.
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.
ECMAScript module support is indicated by the passed in supports*
options. Spécifiquement supportsDynamicImport : true
signifie que le transformateur peut retourner des expressions import()
, qui sont prises en charge par ESM et CJS. Si supportsStaticESM : true
cela signifie que les instructions de premier niveau import
sont prises en charge et que le code sera interprété comme ESM et non comme CJS. Consultez la documentation de Node pour plus de détails sur les différences.
Assurez-vous que la méthode process{Async}
renvoie la source map avec le code transformé, pour qu'il soit possible de reporter les informations de ligne avec précision dans la couverture de code et les erreurs de test. Les source maps en ligne fonctionnent également mais sont plus lentes.
Lors du développement d'un transformateur, il peut être utile d'exécuter Jest avec --no-cache
pour supprimer fréquemment le cache.
Exemples
TypeScript avec vérification de type
Alors que babel-jest
transpile par défaut les fichiers TypeScript, Babel ne vérifie pas les types. Si vous le souhaitez, vous pouvez utiliser ts-jest
.
Transformation des images vers leur chemin
L'importation d'images est un moyen de les inclure dans le paquet de votre navigateur, mais elles ne sont pas du JavaScript valide. Une façon de le gérer dans Jest est de remplacer la valeur importée par son nom de fichier.
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',
},
};