Подтвердить что ты не робот

Укажите код для запуска до того, как произойдет какая-либо установка Jest

Tl; dr:

1) Как я могу заставить Jest использовать встроенную функцию require для загрузки всех модулей в моих тестах где угодно.

2) Где/как я могу изменить (т.е. заменить на загрузчик esm) https://github.com/standard-things/esm функцию require в одном месте, перед выполнением любых тестов, поэтому все тесты будут использовать модифицированное требование.


Я хотел бы использовать esm-loader с моими тестовыми файлами Jest. Чтобы сделать это, мне нужно исправить функцию require во всем мире, прежде чем запускать какой-либо тестовый код, с чем-то вроде

require = require("@std/esm")(module, { esm: "js", cjs: true });

Как сообщить Jest о выполнении этого кода прежде, чем что-либо еще коснется или запросит?

Я попытался указать как массив setupTestFrameworkScriptFile, так и массив setupFiles в файл с ним, но ни один из них не работал (хотя я и подтвердил, что оба они выполнялись).

В качестве альтернативы я увольняю эти тесты с помощью npm script

"scripts": {
  "test": "jest"
}

Есть ли какая-то маска CLI, благодаря которой я могу просто загрузить модуль, а затем запустить jest?


Изменить - опции testEnvironment и resolver заставляют меня задаться вопросом, действительно ли это когда-либо используется с фактической функцией Node require для загрузки модулей или вместо этого используется собственный загрузчик модуля. Если это так, мне интересно, возможно ли это.

4b9b3361

Ответ 1

Так что это было немного сложно, чтобы работать. Решение довольно простое, но мне потребовалось некоторое время, чтобы заставить его работать. Проблема в том, что всякий раз, когда вы используете какой-либо модуль в шутку

  • Файлы настроек
  • Файлы рамочной программы установки
  • Тестовые файлы
  • Файлы модулей

Все они загружены ниже.

({ "Object.": function (module, export, require, __ dirname, __ filename, global, jest) { /* Код модуля внутри */}});

Если вы посмотрите 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); предоставляет каждому модулю пользовательский требуемый объект. Таким образом, вы, как таковые, не получаете родную функцию require вообще. После того, как начнется шутка, каждый загруженный из него модуль будет иметь функцию jest custom require.

Когда мы загружаем модуль, вызываются методы requireModule из jest-runtime. Ниже приведена выдержка из того же

  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);
  }

Как вы можете видеть, если расширение файла .node, он загружает модуль напрямую, иначе он вызывает _execModule. Эта функция является тем же самым кодом, который я опубликовал ранее, что делает преобразование кода

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);

Теперь, когда мы хотим изменить функцию require для нашего теста, нам нужно _execModule экспортировать наш код напрямую. Таким образом, код должен быть похож на загрузку модулей .node

  } else if ((_path || _load_path()).default.extname(modulePath) === '.mjs') {
    // $FlowFixMe
    require = require("@std/esm")(localModule);
    localModule.exports = require(modulePath);
  } else {

Но это будет означать исправление кода, которого мы хотим избежать. Итак, вместо этого мы избегаем непосредственного использования команды jest и создаем собственный jestload.js и запускаем это. Код для загрузки шутки прост

#!/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');

Теперь мы хотим изменить _execModule перед загрузкой cli. Поэтому мы добавляем ниже код

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');

Теперь время для теста

//__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);
});

И файл sum.mjs

export function sum (x, y) { return x + y }

Теперь мы запускаем тест

Jest Test

Решение доступно ниже для репо

https://github.com/tarunlalwani/jest-overriding-require-function-stackoverflow

Вы можете клонировать и тестировать решение, запустив npm test.

Ответ 2

Я попытался использовать node -r @std/esm run.js, где run.js - это просто script, который вызывает шутки, но он не работает и сбой здесь: https://github.com/facebook/jest/blob/master/packages/jest-runtime/src/script_transformer.js#L305.

Из того, что я понимаю из этой строки, это означает, что это невозможно, потому что jest компилирует модуль, используя собственный vm модуль. Вышеуказанные строки (290):

  if (willTransform) {
    const transformedSource = this.transformSource(
    filename,
    content,
    instrument,
    !!(options && options.mapCoverage));


    wrappedCode = wrap(transformedSource.code);
    sourceMapPath = transformedSource.sourceMapPath;
  } else {

- это код, который вызывается при указании преобразований в вашей конфигурации jest.

Заключение: пока поддержка esm (и они будут находиться под расширением .mjs), вы не сможете импортировать модули es в jest без, указав преобразование. Вы можете попробовать патч для обезьяны vm, но я бы действительно советовал против этого варианта.

Задание jest-преобразования на самом деле не так сложно, и для es-модулей это действительно так же просто, как использование babel-jest с правильной конфигурацией babel:

Ниже package.json с минимальными настройками

{
    "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"]
    }
}