Especifique el código a ejecutar antes de que ocurra cualquier configuración de Jest
El tl; dr es:
1) Cómo puedo hacer que Jest use la función nativa require
para cargar todos los módulos en mis pruebas en cualquier lugar.
2) Dónde / cómo podría modificar (es decir, reemplazar con el cargador esm) https://github.com/standard-things/esm la función require en un solo lugar, antes de ejecutar cualquier prueba, por lo que todas las pruebas utilizarán el require modificado.
Me gustaría usar el esm-loader con mis archivos de prueba de Jest. Con el fin de hacerlo, tengo que parchear el requerir la función globalmente, antes de que se ejecute cualquier código de prueba, con algo como
require = require("@std/esm")(module, { esm: "js", cjs: true });
¿Cómo le digo a Jest que ejecute ese código antes de que se toque o solicite algo más?
Intenté apuntar tanto setupTestFrameworkScriptFile
como una entrada de matriz setupFiles
a un archivo con eso en él, pero ninguno funcionó (aunque confirmé que ambos corrían).
Alternativamente, estoy disparando estas pruebas con un script npm
"scripts": {
"test": "jest"
}
¿Hay alguna magia CLI por la que solo puedo cargar un módulo y entonces ejecutar jest
?
Edit - las opciones testEnvironment
y resolver
me hacen preguntarme si esto está incluso usando la función Node require
real para cargar módulos, o en su lugar usando su propio cargador de módulos. Si es así me pregunto si esto es posible.
3 answers
Así que este fue un poco difícil de conseguir trabajar. La solución es bastante simple, pero me llevó un tiempo hacerlo funcionar. El problema es que cada vez que se utiliza cualquier módulo en jest
- Archivos de configuración
- Configurar archivos de marco
- Archivos de prueba
- Archivos de módulo
Todos están cargados por debajo de la manera
({"Object.": function (module,exports,require,__dirname,__filename,global,jest){ / * Module code inside* / }});
Si echas un vistazo a node_modules/jest-runtime/build/index.js:495:510
const dirname = (_path || _load_path()).default.dirname(filename);
localModule.children = [];
localModule.parent = mockParentModule;
localModule.paths = this._resolver.getModulePaths(dirname);
localModule.require = this._createRequireImplementation(filename, options);
const transformedFile = this._scriptTransformer.transform(
filename,
{
collectCoverage: this._coverageOptions.collectCoverage,
collectCoverageFrom: this._coverageOptions.collectCoverageFrom,
collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom,
isInternalModule,
mapCoverage: this._coverageOptions.mapCoverage },
this._cacheFS[filename]);
this._createRequireImplementation(filename, options);
le da a cada módulo un objeto require personalizado. Así que como tal no obtiene la función nativa requerir en absoluto, en cualquier lugar. Una vez que jest ha iniciado cada módulo cargado a partir de entonces tendrá la función personalizada require
de jest.
Cuando cargamos un módulo, se llama a los métodos requireModule
de jest-runtime
. A continuación se muestra un extracto de la misma
moduleRegistry[modulePath] = localModule;
if ((_path || _load_path()).default.extname(modulePath) === '.json') {
localModule.exports = this._environment.global.JSON.parse(
(0, (_stripBom || _load_stripBom()).default)((_gracefulFs || _load_gracefulFs()).default.readFileSync(modulePath, 'utf8')));
} else if ((_path || _load_path()).default.extname(modulePath) === '.node') {
// $FlowFixMe
localModule.exports = require(modulePath);
} else {
this._execModule(localModule, options);
}
Como puede ver si la extensión del archivo es .node
carga el módulo directamente, de lo contrario llama al _execModule
. Esta función es el mismo código que he publicado anteriormente que hace la transformación de código
const isInternalModule = !!(options && options.isInternalModule);
const filename = localModule.filename;
const lastExecutingModulePath = this._currentlyExecutingModulePath;
this._currentlyExecutingModulePath = filename;
const origCurrExecutingManualMock = this._isCurrentlyExecutingManualMock;
this._isCurrentlyExecutingManualMock = filename;
const dirname = (_path || _load_path()).default.dirname(filename);
localModule.children = [];
localModule.parent = mockParentModule;
localModule.paths = this._resolver.getModulePaths(dirname);
localModule.require = this._createRequireImplementation(filename, options);
Ahora cuando queremos modificar la función require
para nuestra prueba, necesitamos _execModule
para exportar nuestro código directamente. Así que el código debe ser similar a la carga de un .node
módulos
} else if ((_path || _load_path()).default.extname(modulePath) === '.mjs') {
// $FlowFixMe
require = require("@std/esm")(localModule);
localModule.exports = require(modulePath);
} else {
Pero hacer eso significaría parchear el código, lo que queremos evitar. Así que lo que hacemos en su lugar es evitar usar el comando jest directamente, y crear nuestro propio jestload.js
y corriendo eso. El código para cargar jest es simple
#!/usr/bin/env node
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
cli = require('jest/bin/jest');
Ahora queremos modificar el _execModule
antes de que se cargue la cli. Así que agregamos el siguiente código
const jestRuntime = require("jest-runtime");
oldexecModule = jestRuntime.prototype._execModule;
jestRuntime.prototype._execModule = function (localModule, options) {
if (localModule.id.indexOf(".mjs") > 0) {
localModule.exports = require("@std/esm")(localModule)(localModule.id);
return localModule;
}
return oldexecModule.apply(this, [localModule, options]);
};
cli = require('jest/bin/jest');
Ahora es el momento de una prueba
//__test__/sum.test.js
sum = require('../sum.mjs').sum;
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('adds 2 + 3 to equal 5', () => {
expect(sum(3, 2)).toBe(5);
});
Y un archivo sum.mjs
export function sum (x, y) { return x + y }
Ahora ejecutamos la prueba
La solución está disponible a continuación repo
Https://github.com/tarunlalwani/jest-overriding-require-function-stackoverflow
Puede clonar y probar el solución ejecutando npm test
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-04 22:22:14
He intentado usar node -r @std/esm run.js
donde ejecutar.js es solo un script que llama a jest, pero no funciona y se bloquea aquí: https://github.com/facebook/jest/blob/master/packages/jest-runtime/src/script_transformer.js#L305.
Por lo que entiendo de esta línea significa que no es posible porque jest compila el módulo usando el módulo nativo vm
. Las líneas anteriores (290):
if (willTransform) {
const transformedSource = this.transformSource(
filename,
content,
instrument,
!!(options && options.mapCoverage));
wrappedCode = wrap(transformedSource.code);
sourceMapPath = transformedSource.sourceMapPath;
} else {
Es el código llamado cuando está especificando transformaciones en su broma config.
Conclusión : hasta que no sean compatibles con esm ( y estarán bajo la extensión .mjs
) no puede importar módulos es en jest sin especificar una transformación. Usted podría tratar de mono parche vm
pero yo realmente aconsejaría en contra de esta opción.
Especificar una transformación de jest no es realmente tan difícil, y para los módulos es es realmente tan simple como usar babel-jest
con la configuración correcta de babel:
Debajo de un paquete.json con ajustes mínimos
{
"dependencies": {
"babel-jest": "^21.2.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"jest": "^21.2.1"
},
"jest": {
"testMatch": [
"<rootDir>/src/**/__tests__/**/*.js?(x)",
"<rootDir>/src/**/?(*.)(spec|test).js?(x)"
],
"transform": {
"^.+\\.(js|jsx)$": "<rootDir>/node_modules/babel-jest"
},
"testEnvironment": "node",
"testURL": "http://localhost",
"moduleFileExtensions": [
"js",
"json"
]
},
"babel": {
"plugins": ["babel-plugin-transform-es2015-modules-commonjs"]
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-04 01:08:56
setupFiles
funcionó para mí. Añadir esto en el paquete.json:
"jest": {
"setupFiles": ["./my_file.js"]
},
Https://jestjs.io/docs/en/configuration.html#setupfiles-array
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-10-04 02:35:14