Как я могу издеваться над Webpack require.context в Jest?

Предположим, что у меня есть следующий модуль:

var modulesReq = require.context('.', false, /\.js$/);
modulesReq.keys().forEach(function(module) {
  modulesReq(module);
});

Jest жалуется, потому что не знает о require.context:

 FAIL  /foo/bar.spec.js (0s)
● Runtime Error
  - TypeError: require.context is not a function

Как я могу издеваться над этим? Я попытался использовать setupTestFrameworkScriptFile настройку Jest, но тесты не могут увидеть никаких изменений, которые я сделал в require.

Ответы

Ответ 1

У меня была такая же проблема, тогда я нашел "решение".

Я уверен, что это не лучший выбор. Я закончил тем, что прекратил использовать это, ответив на вопросы здесь:

https://github.com/facebookincubator/create-react-app/issues/517 https://github.com/facebook/jest/issues/2298

Но если вам это действительно нужно, вы должны включить приведенный ниже полифилл в каждый файл, который вы вызываете (не в сам файл тестов, потому что require не будет переопределено глобально в среде Node).

// This condition actually should detect if it an Node environment
if (typeof require.context === 'undefined') {
  const fs = require('fs');
  const path = require('path');

  require.context = (base = '.', scanSubDirectories = false, regularExpression = /\.js$/) => {
    const files = {};

    function readDirectory(directory) {
      fs.readdirSync(directory).forEach((file) => {
        const fullPath = path.resolve(directory, file);

        if (fs.statSync(fullPath).isDirectory()) {
          if (scanSubDirectories) readDirectory(fullPath);

          return;
        }

        if (!regularExpression.test(fullPath)) return;

        files[fullPath] = true;
      });
    }

    readDirectory(path.resolve(__dirname, base));

    function Module(file) {
      return require(file);
    }

    Module.keys = () => Object.keys(files);

    return Module;
  };
}

С помощью этой функции вам не нужно изменять какой-либо вызов require.context, он будет выполняться с тем же поведением, что и при выполнении (если он используется в веб-пакете, он просто использует оригинальную реализацию, а если он находится внутри выполнения Jest - с полифиллом) функция).

Ответ 2

Извлеките вызов отдельному модулю:

// src/js/lib/bundle-loader.js
/* istanbul ignore next */
module.exports = require.context('bundle-loader?lazy!../components/', false, /.*\.vue$/)

Используйте новый модуль в модуле, где вы его извлекли:

// src/js/lib/loader.js
const loadModule = require('lib/bundle-loader')
// ...

Создайте макет для вновь созданного модуля-загрузчика:

// test/unit/specs/__mocks__/lib/bundle-loader.js
export default () => () => 'foobar'

Используйте макет в своем тесте:

// test/unit/specs/lib/loader.spec.js
jest.mock('lib/bundle-loader')
import Loader from 'lib/loader'

describe('lib/loader', () => {
  describe('Loader', () => {
    it('should load', () => {
      const loader = new Loader('[data-module]')
      expect(loader).toBeInstanceOf(Loader)
    })
  })
})

Ответ 3

Если вы используете Babel, посмотрите на babel-plugin-require-context-hook. Инструкции по настройке для Storybook доступны на Storyshots | Настройте Jest для работы с Webpack require.context(), но они не относятся к Storyshots/Storybook.

Чтобы подвести итог:

Установите плагин.

yarn add babel-plugin-require-context-hook --dev

Создайте файл .jest/register-context.js со следующим содержимым:

import registerRequireContextHook from 'babel-plugin-require-context-hook/register';
registerRequireContextHook();

Настройте Jest (файл зависит от того, где вы храните конфигурацию Jest, например package.json):

setupFiles: ['<rootDir>/.jest/register-context.js']

Добавьте плагин в .babelrc

{
  "presets": ["..."],
  "plugins": ["..."],
  "env": {
    "test": {
      "plugins": ["require-context-hook"]
    }
  }
}

Или добавьте его в babel.config.js:

module.exports = function(api) {
  api.cache(true)

  const presets = [...]
  const plugins = [...]

  if (process.env.ENV_NODE === "test") {    
    plugins.push("require-context-hook")    
  }

  return {
    presets,
    plugins
  }
}

Возможно, стоит отметить, что использование babel.config.js вместо .babelrc может вызвать проблемы. Например, я обнаружил, что когда я определил плагин require-context-hook в babel.config.js:

  • Шут 22 не поднял это;
  • Шут 23 поднял это; но
  • jest --coverage не подняла его (возможно, Стамбул не в ногу с Бабелем 7?).

Во всех случаях конфигурация .babelrc была в порядке.


Замечания по Эдмундо Родригесу ответ

Этот babel-plugin-require-context-hook использует код, подобный ответу Эдмундо Родригеса здесь. Реквизит Эдмундо! Поскольку плагин реализован как плагин Babel, он позволяет избежать проблем статического анализа. Например, с помощью решения Edmundo Webpack предупреждает:

Critical dependency: require function is used in a way in which dependencies cannot be statically extracted

Несмотря на предупреждения, решение Edmundo является наиболее надежным, поскольку оно не зависит от Babel.

Ответ 4

Установка

столпотворение-плагин-преобразование-требуют контексты

пакет и добавление плагина в .babelrc решили проблему для меня. Обратитесь к документации здесь: https://www.npmjs.com/package/babel-plugin-transform-require-context