Jest: Мета: встроенная поддержка модулей ES

Созданный на 19 янв. 2020  ·  131Комментарии  ·  Источник: facebook/jest

РЕДАКТИРОВАТЬ: краткое руководство для начала: https://jestjs.io/docs/en/ecmascript-modules

Поддержка ESM будет отменена в следующем выпуске Node 12 (возможно, не ранее апреля https://github.com/nodejs/node/pull/29866#issuecomment-574055057), и она уже не отмечена в Node 13.2, поэтому я думаю, что это пора оценить, как мы можем добавить встроенную поддержку в Jest. Я попытаюсь перечислить, какие функции, которые в настоящее время предоставляет Jest, зависят от поддержки ESM, и как мы можем решить / исследовать их.

Существует проблема № 4842, но я думаю, что это скорее вопрос обсуждения, в то время как этот вопрос будет направлен на фактическую реализацию поддержки и более удобен для отслеживания для тех, кто просто хочет получить текущий статус реализации. Любые комментарии, добавленные к этой проблеме, _не_ относящиеся к тому, как мы можем реализовать поддержку перечисленных ниже функций, будут помечены как спам - просьба направлять любые обходные пути / обсуждения в отдельные проблемы. Также не стесняйтесь сообщать нам, если что-то, связанное с функциями ESM, отсутствует в списке!

Обратите внимание, что Jest будет использовать vm API (https://nodejs.org/api/vm.html), и на момент написания (узел v13.6) части ESM этого API все еще помечены ( --experimental-vm-modules ). Так что говорить о том, что ESM не помечен, на данный момент звучит неправильно. Но я думаю, что мы должны начать экспериментировать и, возможно, предоставить обратную связь с WG по

Наконец, я пишу этот выпуск в основном для людей, которые будут реализовывать поддержку, поэтому он будет несколько низкоуровневым и специфичным для того, как работает Jest. Для людей, которые _ просто_ хотят знать, была ли получена поддержка или нет, я рекомендую использовать замечательное «настраиваемое уведомление» GH и подписываться на уведомления только при закрытии / повторном открытии.


  • [x] Запуск модуля в правильном контексте

Мы достигаем песочницы, выполняя скрипт в заданном vm.Context (предоставляемом JSDOM или API ядра узла). Нам нужно сделать то же самое для ESM, но нам понадобится доступ к context во время построения модуля, а не только при его выполнении. Я открыл # 9428, который добавляет необходимые API в JestEnvironment .

  • [x] Глобалы

expect , test , beforeEach т. Д. Будут добавлены как глобальные переменные, здесь ничего не должно измениться. jasmine global тоже будет здесь.

  • [x] jest "глобальное" свойство

На самом деле это не глобально - он вводится в область видимости модуля. Поскольку в ESM область видимости модуля отсутствует, нам нужно куда-то ее переместить. Добавление его к import.meta кажется естественным - есть опция initializeImportMeta которую мы можем использовать.

РЕДАКТИРОВАТЬ: Решение здесь - получить его через import {jest} from '@jest/globals' . Мы могли бы добавить его через import.meta в будущем, но на данный момент этого должно быть достаточно.

  • [] jest.(do|un)mock

Поскольку ESM имеет разные «стадии» при оценке модуля, jest.mock не будет работать для статического импорта. Однако он может работать для динамического импорта, поэтому я думаю, что нам просто нужно четко указать в документации, что он поддерживает, а что нет.

jest.mock вызовы import 'thing' в import('thing') что должно позволить подъему работать, но тогда он будет асинхронным. Использование верхнего уровня await , вероятно, является необходимостью для такого подхода. Я также думаю, что это достаточно агрессивно, чтобы оправдать отдельный вариант. Есть кое-что для обсуждения - нам не нужно поддерживать все, что может jest.mock для первоначального выпуска.

  • [] jest.requireActual

Не уверен, как он должен себя вести в ESM. Должны ли мы предоставить jest.importActual и позволить requireActual CJS всегда оценивать в

  • [x] import.meta

Единственным свойством Node является url (по крайней мере, на данный момент). Нам нужно убедиться, что он также заполнен в Jest. Мы предоставляем identifier вместо filename при построении модуля, поэтому я не думаю, что это произойдет автоматически, но url по сути означает filename пройденное через pathToFileURL .

Также есть открытый PR для import.meta.resolve : https://github.com/nodejs/node/pull/31032

  • [x] import thing from 'thing'

На самом деле это должно быть довольно просто, нам просто нужно реализовать linker где мы также можем преобразовать источник перед его возвратом, что означает, что нам не нужен API загрузчика (который еще не существует). Это также позволяет нам возвращать моки (хотя они должны быть получены из каталога __mocks__ ).

  • [x] import('thing')

По сути то же самое, что и выше, но передается как importModuleDynamically при создании модуля. Также будет поддерживать более чистую поддержку jest.mock , jest.resetModules т. Д., Поэтому, вероятно, будет использоваться совсем немного.

Это также можно сделать для vm.Script помощью той же опции.

  • [] Обработка ошибок во время оценки

Прямо сейчас это ошибка времени выполнения (например, модуль не найден), но это не обязательно верно для ESM. Для нас это важно? Мы должны убедиться, что ошибки по-прежнему выглядят красиво.

  • [x] module.createRequire

Нам нужно иметь дело с этим для людей, желающих использовать CJS от ESM. Я открыл # 9426, чтобы отслеживать это отдельно, поскольку его реализация на самом деле не связана с поддержкой ESM.

РЕДАКТИРОВАТЬ: реализовано в # 9469

  • [] module.syncBuiltinESMExports

https://nodejs.org/api/modules.html#modules_module_syncbuiltinesmexports. Нам это небезразлично, или мы просто делаем его бесполезным? Не уверен, каков будет вариант использования Jest. Возня со встроенными командами уже ломает песочницу, и я не думаю, что это должно иметь значение.

РЕДАКТИРОВАТЬ: # 9469 сделал это запретным. Думаю, это нормально?

  • [] Определить, должен ли файл находиться в режиме ESM или CJS

Проверка поля type в модуле package.json кажется разумной: https://nodejs.org/api/esm.html#esm_enhibited. Должны ли мы также иметь собственный флаг конфигурации? Также необходимо учитывать окончания файлов.

https://github.com/nodejs/modules/issues/393

  • [x] moduleNameMapper

Не уверен, что это повлияет на что-нибудь. Я _не думаю_, потому что мы сами будем связывать модули вместе. Тем не менее, требует расследования.

РЕДАКТИРОВАТЬ: это вся логика разрешения, которую мы контролируем. Так что здесь никаких изменений.

  • [x] jest.config.mjs

Через # 9291 мы поддерживаем jest.config.cjs - нужно ли нам делать что-то особенное для .mjs ? Вероятно, используйте import('path/to/configFile.mjs') что означает, что он должен быть асинхронным. Это проблема? Возможно, стоит сделать разрешение конфигурации async в Jest 25, чтобы оно не блокировало инкрементную поддержку ESM в Jest 25.

РЕДАКТИРОВАТЬ: # 9431

  • [] Экспорт пакетов

Node поддерживает экспорт пакетов , который сортируется с moduleNameMapper Jest, но также предоставляет функции инкапсуляции. Надеюсь, resolve реализует это, но если они этого не сделают, нам нужно что-то сделать. Может быть достаточно использовать опцию pathFilter ? Не уверен.

  • [] Модуль JSON / WASM

https://nodejs.org/api/esm.html#esm_experimental_json_modules. Нам нужно заботиться? Наверное, особенно за json . Для нас тривиально поддерживать import thing from './package.json' поскольку мы контролируем фазу связывания, но мы, вероятно, не должны делать это по умолчанию, поскольку он будет отличаться от узла по умолчанию. Должны ли мы заставить людей определять трансформацию для него?

  • [x] Покрытие кода

Это имеет значение? Я не думаю, что это повлияет на это, поскольку мы все еще можем преобразовать источник с помощью babel (возможно, он будет сбит с толку операторами import , возможно, нет), и покрытие V8 определенно не должно заботиться. Однако мы должны проверить.

  • [] Разрешение асинхронного кода

Это абсолютно не блокировщик, так как разрешение синхронизации будет работать нормально. Но теперь мы _ можем_ использовать асинхронное разрешение, и это здорово. Интересно, стоит ли нам снова рассмотреть возможность использования модуля resolve вне npm, поскольку он уже поддерживает асинхронный режим. См. №9505.

  • [] Преобразование асинхронного кода

Как и выше, не блокирует, но неплохо бы его поддержать. Может сделать @jest/transformer более удобным для использования в других средах. См. №9504.

  • [] Плохая производительность при доступе к глобальным объектам

Из-за # 5163 у нас есть вариант extraGlobals в качестве обходного пути - этот обходной путь больше не применим в ESM. Я открыл и проблема с узлом здесь: https://github.com/nodejs/node/issues/31658

ES Modules

Самый полезный комментарий

Я получил базовую поддержку с # 9772. Я тестировал только самые простые случаи, и есть много известных ограничений (в первую очередь отсутствие поддержки объекта jest и нарушение семантики при смешивании CJS и ESM), но, по крайней мере, это _something_. Он выйдет в следующем выпуске Jest (надеюсь, скоро, заблокирован только # 9806)

Все 131 Комментарий

Я получил базовую поддержку с # 9772. Я тестировал только самые простые случаи, и есть много известных ограничений (в первую очередь отсутствие поддержки объекта jest и нарушение семантики при смешивании CJS и ESM), но, по крайней мере, это _something_. Он выйдет в следующем выпуске Jest (надеюсь, скоро, заблокирован только # 9806)

25.4.0 был выпущен с первыми частями поддержки. В дополнение к # 9772, упомянутому выше, я также включил # 9842. В _theory_ смешивание CJS и ESM теперь должно работать правильно (🤞).

Единственная основная недостающая функция - поддержка объекта jest . Я не решил, следует ли нам придерживаться import.meta или требовать, чтобы люди импортировали его через import {jest} from '@jest/globals' . Обратная связь приветствуется!

Я еще не написал для этого документацию, но для ее активации вам нужно сделать 3 вещи

  1. убедитесь, что вы не запускаете преобразование из операторов import (установите transform: {} в конфигурации или иным образом убедитесь, что babel не преобразует файл в CJS, например, избегая modules вариант для preset-env)
  2. Запустить node@^12.16.0 || >=13.2.0 с флагом --experimental-vm-modules
  3. Запустите тест с jest-environment-node или jest-environment-jsdom-sixteen

Пожалуйста, попробуйте и оставьте отзыв! Если вы сообщаете об ошибках, было бы замечательно, если бы вы также могли указать, как запуск того же кода (за вычетом любого кода, специфичного для теста) выполняется в Node. Я читал https://nodejs.org/api/esm.html _a lot_ за последние несколько недель, но, вероятно, что-то упустил.

Единственная основная недостающая функция - поддержка объекта-шутки. Я не решил, следует ли нам прикрепить его к import.meta или потребовать от людей импортировать его через import {jest} из '@ jest / globals'.

Для сценария использования машинописного текста лучше иметь явный импорт.

Да, я добавил (и временно отменил) пакет @jest/globals , который поддерживает это, так что он будет доступен независимо. Мне интересно, имеет ли смысл _также_ выставлять его на import.meta . В настоящее время склоняюсь к тому, чтобы этого не делать, в основном потому, что позже легче добавить, чем удалить (и я лично не фанат глобальных объектов)

+1 для явного импорта, это немного более подробно, но проще для понимания

Я получаю это в Node 13.2 и Jest 25.4: ES Modules are only supported if your test environment has the getVmContext function Что мне не хватает?

@zandaqo Извините, я забыл об этом. Добавлено выше, но это

Запустите свои тесты с jest-environment-node или jest-environment-jsdom-sixteen

ReferenceError: jest is not defined Полагаю, это связано с отсутствием @jest/globals

Да, как уже упоминалось, это будет работать, только если вы не используете объект jest .
Моки тоже наверно битые, не тестировал 😃

Я скомпилировал очень простой проект из того, что я вижу в каталоге тестов e2e ( e2e/native-esm/__tests__/native-esm.test.js ) и в этом выпуске. И, к сожалению, я все еще не могу заставить его работать 🙃 Может ли кто-нибудь проверить это случайно?

https://drive.google.com/file/d/1vyDZjsVKOTu6j55QA11GjO9E7kM5WX8_/view?usp=sharing

  • [x] jest версии 25.4.0
  • [x] версия узла v13.12.0
  • [x] package.json содержит рекомендуемые параметры шутки, и, похоже, нет никаких преобразований babel

Запуск примера скрипта (просто импорт функции double и печать double(2) ):

npm run main

> [email protected] main /Users/ilya/Projects/jest-esm
> node src/main.js

(node:16961) ExperimentalWarning: The ESM module loader is experimental.
4

Запуск шутки с помощью всего одного теста на двойную функцию:

npm run test

> [email protected] test /Users/ilya/Projects/jest-esm
> jest

 FAIL  __tests__/native-esm.test.js
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /Users/ilya/Projects/jest-esm/__tests__/native-esm.test.js:8
    import {double} from '../src/index.js';
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      at Runtime._execModule (node_modules/jest-runtime/build/index.js:1074:58)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.542s
Ran all test suites.

Вам нужно запустить узел с --experimental-vm-modules и назвать свой файл .mjs или "type": "module" в package.json .

РЕДАКТИРОВАТЬ: у вас, вероятно, есть последнее, видя, как оно работает за пределами Jest для вас, просто отсутствует экспериментальный флаг для Node

@SimenB о, вау, есть --experimental-vm-modules AND --experimental-modules . Меня смутил тот факт, что --experimental-modules не требуется, начиная с какой-то версии узла 13. Спасибо!

TL; DR: мне подходит node --experimental-vm-modules node_modules/jest/bin/jest.js

Да, из ОП

Обратите внимание, что Jest будет использовать vm API (https://nodejs.org/api/vm.html), и на момент написания (узел v13.6) части ESM этого API все еще помечены ( --experimental-vm-modules ). Так что говорить о том, что ESM не помечен, на данный момент звучит неправильно.

Но все же замечательно, что это работает для вас!

(Я отмечу эти комментарии как решенные)

@SimenB Спасибо за это! Я пока вижу две проблемы.

Выпуск 1

  • Файл модульного теста ESM импортирует по умолчанию из файла ESM (проверяется fn)
  • Тестируемый файл ESM по умолчанию импортирует из пакета, который экспортирует только CJS.
  • Результат с ошибкой: ReferenceError: module is not defined в файле пакета CJS

Думаю, это может быть неправильно реализовано?

Оператор импорта может ссылаться на модуль ES или модуль CommonJS.

Это отлично работает при запуске приложения. Именованный экспорт из CJS можно импортировать только с помощью createRequire, но экспорт по умолчанию можно просто импортировать.

Выпуск 2

Когда я не получаю указанную выше ошибку, я нажимаю эту:

TypeError: _vm(...).SyntheticModule is not a constructor

      at Runtime._importCoreModule (node_modules/jest-runtime/build/index.js:1198:12)

Подробности проекта

  • Узел 12.14.1
  • Шутка и бабель-шутка 25.4.0
  • Последний Babel с targets: { node: 12 } и двумя плагинами: babel-plugin-transform-import-meta и rewire-exports . (Пытался удалить плагин import-meta но получил сообщение об ошибке добавить его обратно.)
  • testEnvironment: "node" - почти единственная конфигурация
  • node --experimental-vm-modules node_modules/jest/bin/jest.js

Если репо с воспроизведением будет полезно, дайте мне знать.

Спасибо @aldeed!

Я рассмотрю вопрос 1, который действительно выглядит как ошибка. РЕДАКТИРОВАТЬ: должно быть исправлено через # 9850

Проблема 2 требует узла 12.16.0: https://nodejs.org/docs/latest-v12.x/api/vm.html#vm_class_vm_syntheticmodule

Я изменю чек в Jest (прямо сейчас он проверяет vm.SourceTextModule который доступен в других версиях, но нам также понадобится SyntheticModule ).

Подтверждено, что работа с 12.16.0 устраняет мою проблему 2. После выпуска исправления проблема 1 будет проверена повторно. В противном случае ожидаю объект jest для дальнейшего тестирования, и я согласен с тем, что его необходимо импортировать.

Отличная работа, @SimenB! Я пробую это на небольшом проекте, но столкнулся с проблемами с динамическим импортом. Это ошибка, которую я вижу:

Module status must not be unlinked or linkingError [ERR_VM_MODULE_STATUS]: Module status must not be unlinked or linking

Если я удалю динамический импорт, тест будет запущен (но, конечно, не удастся по другим причинам). Тот же тест в настоящее время работает с Mocha (который недавно предоставил поддержку ESM).

Если это поможет, рассматриваемый динамический импорт можно увидеть здесь: https://github.com/beejunk/firn.js/blob/switch-to-jest/lib/renderPage.js#L43 -L55

Тестовый файл находится здесь: https://github.com/beejunk/firn.js/blob/switch-to-jest/test/build.test.js. Работающую версию Mocha можно увидеть в основной ветке.

Дайте мне знать, если есть еще какая-нибудь информация, которая будет мне полезна.

Спасибо @beejunk! Мне было интересно, могут ли быть условия гонки между import s, которые импортируют один и тот же модуль до того, как он будет полностью связан. Кажется, это то, что ты здесь делаешь. Исправлю сегодня, спасибо за отчет!

РЕДАКТИРОВАТЬ: исправлено в # 9858. Скопировал исправление в ваше репо:
image

Кому-нибудь удалось заставить это работать с TypeScript? node --experimental-vm-modules node_modules/jest/bin/jest.js возвращает тот же SyntaxError: Cannot use import statement outside a module , хотя в моем package.json есть "type": "module" .

babel.config.cjs

module.exports = {
  presets: [
    '@babel/preset-typescript',
  ],
};

jest.config.cjs

module.exports = {
  testEnvironment: 'jest-environment-node',
  transform: {},
};

@dandv По сути, вы попали в этот случай: https://github.com/facebook/jest/pull/9772/files#r407255029

Не могли бы вы поднять отдельный вопрос? Нужно будет выяснить, что делать с расширениями, отличными от js.

@SimenB : # 9860. Спасибо, что посмотрели.

Выпущен @aldeed @beejunk 25.5.0 с исправлениями ваших проблем. Продолжайте сообщать об ошибках 😀

О, кроме того, он включает поддержку import { jest } from '@jest/globals' для тех из вас, кто этого ждет 👍

Спасибо за быструю работу над всем этим, @SimenB! Я думаю, что столкнулся с другой проблемой.

Я экспериментировал с использованием параметров запроса в пути импорта в качестве способа перебора кеша модуля на сервере разработки. Идея состоит в том, чтобы иметь наблюдатель файлов, который обнаруживает изменения в компоненте, а затем обновляет путь импорта с произвольным параметром запроса, чтобы новый код немедленно вставлялся при следующей загрузке страницы без необходимости перезапуска сервера. Это работает при запуске сервера разработки. Однако Jest выдает следующую ошибку:

Cannot find module '/path/to/project/components/BasePage.js?cache=0' from 'renderPage.js'

Вот пример использования параметров запроса: https://github.com/beejunk/firn.js/blob/switch-to-jest/lib/renderPage.js#L55 -L56

Если я удалю параметры запроса, тесты пройдут, хотя и не последовательно. Если я запускаю один набор (например, npm test -- test/build.test.js ), тесты проходят, но если я запускаю все тесты сразу, они большую часть времени терпят неудачу с неоднозначными ошибками. Я все еще разбираюсь в проблеме с несовместимыми тестами, но подумал, что сначала сообщу о проблеме с параметрами запроса.

Спасибо за отчет @beejunk. # 6282 должен иметь дело с этим, но он также хочет передать запрос трансформаторам и тому подобное, которые нам здесь не нужны. Так что, возможно, имеет смысл просто поддерживать запрос внутри во время выполнения, и пусть # 6282 просто занимается передачей этого запроса.

Я добавил немного кода, чтобы кеш модуля изменялся в зависимости от запроса: https://github.com/facebook/jest/blob/d425a49bd575e7167bc786f3c4f2833589091fa1/packages/jest-runtime/src/index.ts#L330 -L334

Но никакой код никогда не вызывает это с запросом. Я думаю, мы должны просто сделать resolvePath.split('?') и все должно работать.

Что касается несовместимых ошибок, я посмотрю, воспроизводит ли это репо. Я не тестировал код ESM с тестами параллельно, только один тест. Я не уверен, почему это повлияет на вещи, но кто знает 😀

Запрос @beejunk исправлен в 25.5.1. У меня еще не было времени исследовать другую проблему

У меня проблема, которая, я думаю, может быть связана с этим, но не исправлена ​​в 25.X .

Я постараюсь резюмировать сценарий ниже:

  • У вас есть тест с установочным скриптом
  • Этот сценарий установки потребует файл динамически, например:
    { default: generator } = require(path.resolve(f))
  • Все внутри f не передается, вызывая «непредвиденную ошибку импорта идентификатора».

Это также происходит, если я пытаюсь использовать import ()

Поскольку вы упомянули транспиляцию; если у вас есть установка, которая переводит import в require эта проблема не в том месте - эта проблема касается встроенной поддержки.


Тем не менее, вы не можете require ESM, и я еще не смог добавить поддержку import() из CJS из-за того, что Node не имеет для этого API. Поддержка этого реализована на Node master, но еще не выпущена: https://github.com/nodejs/node/pull/32985. Как видно из связанных PR релизов, он будет в версиях 13.14.0 и 14.1.0. На этом этапе я реализую для него поддержку 👍

Однако вы должны иметь возможность использовать установочный файл .mjs .

@SimenB Это здорово, похоже, сейчас работает в нескольких тестовых файлах! Это небольшой процесс для устранения различий между импортом Babel и Node в каждом файле, добавления импорта jest и т. Д., Поэтому я могу столкнуться с другими проблемами, когда я делаю это в сотнях тестовых файлов.

Еще несколько вопросов:

  • То, что вы сказали в своем предыдущем комментарии о поддержке import() от cjs, позволит ли это также назвать файл конфигурации Jest jest.config.js ? В настоящее время он работает для меня только с именем jest.config.cjs и использованием module.exports = (ошибка: TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified )
  • Имя «@ jest / globals» не соответствует правилу eslint node/no-extraneous-import из-за того, что не является пакетом, указанным в package-lock . Есть ли причина для этого соглашения об именах? Можно ли использовать jest без @ ? Правило легко игнорировать, но мне просто интересно.
  • Связанный вопрос, почему jest global отличается от волшебных fns, таких как test , describe , before и т. Д.? Следует ли все это импортировать сейчас?
  • Когда это будет завершено, сможет ли Jest установить флаг --experimental-vm-modules ? Это как бы заставляет это казаться нестандартным, если оно не работает только с командой jest . Если вы не можете напрямую передавать флаги, возможно, вы можете установить / изменить переменную NODE_OPTIONS env?
  • Теперь я, кажется, получаю немного другую ошибку, чем раньше, когда пробовал это на более старом узле, но ни одна из ошибок не была очевидна, что проблема была минимальной версией узла. Легко ли добавить проверку минимальной версии с более полезным сообщением об ошибке?

позволит ли это также назвать файл конфигурации Jest jest.config.js?

Его можно назвать .js если его ближайший package.json имеет type: 'module' . В противном случае, чтобы использовать ESM в файле конфигурации, его нужно назвать .mjs . См. Https://nodejs.org/api/esm.html#esm_enpting. Обратите внимание, что файл конфигурации работает вне Jest, поэтому у нас нет особого контроля. На данный момент мы не поддерживаем асинхронную конфигурацию, но await с экспортированным обещанием было бы тривиально, PR welcome 🙂 # 8357 застопорился.

Я очень удивлен, что вы получили A dynamic import callback was not specified но мы не загружаем файл конфигурации в виртуальную машину ... Не могли бы вы открыть отдельную проблему?

Имя «@ jest / globals» не соответствует правилу eslint node/no-extraneous-import из-за того, что не является пакетом, указанным в package-lock . Есть ли причина для этого соглашения об именах? Можно ли использовать jest без @ ? Правило легко игнорировать, но мне просто интересно.

Вы должны добавить devDependency к @jest/globals . Сам пакет - это исключительно определения типов. Это отдельный пакет от jest поэтому определения типов работают правильно, и поэтому мы можем выдать ошибку, если он загружен вне среды выполнения Jest.

В настоящее время у меня нет никаких планов менять это, но мы могли бы отказаться от этого пакета и экспортировать типы из jest . Однако это серьезное изменение, так что посмотрим позже 🙂

Связанный вопрос, почему jest global отличается от волшебных fns, таких как test , describe , before и т. Д.? Следует ли все это импортировать сейчас?

jest похож require объект module из CJS в том, что он уникален для каждого файла, а не на самом деле глобальный (например, globalThis.jest === undefined ). Это позволяет jest.mock('../../file.js') т. Д. Работать правильно относительно файла, в который он был внедрен. Вы также можете импортировать фактические глобальные переменные, но они по-прежнему доступны как globalThis.expect и т. Д.

Когда это будет завершено, сможет ли Jest установить флаг --experimental-vm-modules ?

Я думаю, мы подождем, пока узел не установит их, а не установит их молча - они помечены по какой-то причине, API может измениться в какой-то момент. Мы могли бы добавить опцию, которая устанавливает NODE_OPTIONS я думаю, не уверены, изменит ли это текущую среду выполнения? Тем не менее, в настоящее время планов на это нет.

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

Да, я добавлю более подробное сообщение об ошибке вместе с некоторой документацией, когда реализация немного стабилизируется. Поскольку реализация защищена экспериментальным флагом, я не думаю, что кто-то споткнется о ней.

@SimenB , Я обновил Jest до 25.5.2 и теперь все тесты проходят. Параметры запроса работают, и периодические ошибки, которые я видел ранее, больше не возникают. Еще раз спасибо за вашу работу!

Хорошо, я снова увидел ошибки во время последнего запуска тестов, так что это все еще происходит. Я посмотрю, смогу ли я найти способ постоянно воспроизводить и сообщать об этом.

Я считаю, что нашел последовательный способ воспроизвести проблему. Вот ветка, с которой я работаю: https://github.com/beejunk/firn.js/tree/switch-to-jest

Воспроизвести:

  1. Удалите кеш Jest, если он существует. Я просто вручную удалил /tmp/jest_rs .
  2. Выполните npm test трижды. Первые два прогона должны быть успешными. Однако третий прогон не удастся.

Это трассировка стека, которую я вижу, когда возникает ошибка:

    Error: 
        at invariant (/home/brian/Projects/firn.js/node_modules/jest-runtime/build/index.js:1866:11)
        at Runtime.loadEsmModule (/home/brian/Projects/firn.js/node_modules/jest-runtime/build/index.js:480:7)
        at Runtime.linkModules (/home/brian/Projects/firn.js/node_modules/jest-runtime/build/index.js:548:19)
        at importModuleDynamicallyWrapper (internal/vm/module.js:397:21)
        at htmPreactPath (internal/process/esm_loader.js:31:14)
        at renderPage (/home/brian/Projects/firn.js/lib/renderPage.js:53:15)
        at map (/home/brian/Projects/firn.js/lib/build.js:43:12)
        at Array.map (<anonymous>)
        at build (/home/brian/Projects/firn.js/lib/build.js:36:43)
        at Object.<anonymous> (/home/brian/Projects/firn.js/test/build.test.js:57:5)

Ошибка, похоже, связана с динамическим импортом. Сообщение об ошибке отсутствует, поэтому я не совсем уверен, что происходит.

Дополнительное примечание: если я очищу кеш Jest и обновлю тестовый сценарий, добавив --no-cache , то я не смогу воспроизвести проблему.

Хех, я там поленился и не предоставил правильного сообщения об ошибке. Проблема в том, что тестовая среда была разрушена, поэтому я предполагаю, что где-то отсутствует await . Я не видел ничего, просматривая ваш код, так что мне придется копать еще

@SimenB Вот воспроизведение этой проблемы с конфигурацией ESM: https://github.com/facebook/jest/issues/9935

@SimenB Я создал минимальный пример воспроизведения упомянутых выше ошибок Jest при использовании динамического импорта:

https://github.com/beejunk/jest-esm-dynamic-import-error

Спасибо за отличное воспроизведение @beejunk! Я провел больше часов, о чем хочу признаться, не понимая, является ли это ошибкой в ​​Jest или в Node. Я воспроизвел поведение, используя только модули ядра узла, и сообщил об этом в восходящем направлении, так что давайте посмотрим, что они говорят: https://github.com/nodejs/node/issues/33216

Спасибо, что изучили это, @SimenB. Кажется, что тесты проходят стабильно, если я добавляю флаг --no-cache , что подходит для моего случая использования. Ценю всю работу!

Да, я тоже это заметил. Я думаю, что это какая-то проблема с синхронизацией - без кеша все работает достаточно медленно, чтобы работать.

@SimenB Спасибо за решение # 9935. Я упомянул здесь вторую проблему, которая, я думаю, все еще актуальна. Когда type: "module" , jest --init все еще генерирует файл конфигурации с module.exports в нем. Это относительно несущественно, чтобы изменить вручную, если вы знаете, что делаете, но я думаю, что если ESM не отмечен в Node и многие люди начнут выполнять проекты ESM, это начнет больше походить на сбивающую с толку ошибку (т.е. jest --init && jest в новом проекте ESM выдаст ошибку). Должен ли я представить другую проблему, касающуюся улучшения логики инициализации?

@aldeed ты уверен? Прямо сейчас тестирование дает мне mjs файл с export default в нем. Думаю, мы могли бы сгенерировать js а не mjs , но все же. Он использует синтаксис ESM

@SimenB Ну, я был уверен, пока ты не спросил. 😄 Я пробовал, и вы правы. Может, я изначально делал это с более старой версией Node или Jest? Игнорировать.

Это круто! Просто переработал тесты в одной из моих библиотек для использования модулей ES и отказался от Babel. Спасибо @SimenB!

Определить, должен ли файл находиться в режиме ESM или CJS

Говоря об этом, существует множество ориентированных на браузер / сборщик пакетов, в которых используется синтаксис "module":"<path to es module>" для обозначения экспорта своих модулей ES. Было бы разумно иметь какой-то способ указать, как разрешить данный пакет, независимо от собственных настроек пакета. Что-то вроде moduleNameMapper но с указанием CJS или ESM.

привет @SimenB , как только эта проблема будет закрыта, это означает, что ts-jest также может отменить принудительное действие commonjs верно? Требуется ли изменение расширения файла со стороны трансформатора для работы с esm?

Например, теперь ts-jest компилирует ts в js для commonjs , требует ли esm расширения файла mjs при компиляции из ts в js ?

@zandaqo мы не будем поддерживать поле modules , мы будем следовать спецификации узла и использовать exports : # 9771. Вы можете подключить свой собственный преобразователь для поддержки modules если хотите: https://jestjs.io/docs/en/configuration#resolver -string. Мы могли бы добавить какой-нибудь другой вариант ( mainFields , например, webpack, может быть?), Но это произойдет дальше, когда реализация стабилизируется и у нас будет меньше неизвестных неизвестных 🙂


@ahnpnl # 9860

Чао, ребята!
Просто вопрос: в сообщении в блоге упоминается, что, поскольку модули ES6 статичны, их нельзя высмеивать; Итак, на самом деле, нет никакого способа имитировать модуль A, импортированный модулем B в ES6?

@gabrieledarrigo Я использую для этого moduleNameMapper , например:

    "moduleNameMapper": {
      "moduleA": "<rootDir>/test/moduleA-mock.js"
    },

@gabrieledarrigo ты можешь сделать

jest.mock('the-thing-i-want-to-mock', () => /* do whatever in here */);

let importedThing;

beforeAll(async () => {
  importedThing = await import('thing-that-imports-mocked-module');
});

Так что вам просто нужно сделать импорт нестатичным, и насмешка будет работать.

Я не уверен, что сейчас это работает, так как я еще не подключил макеты разрешений в пути кода ESM. Сделаю это в ближайшее время. Но это _ вероятно_ будет тем, как мы документируем макеты модулей для нативного ESM.

Как упоминалось в блоках блога, в какой-то момент мы будем задокументировать эти шаблоны, нам просто нужно с ними разобраться 🙂

Одна из наших идей заключалась в том, чтобы дождаться ожидания верхнего уровня, а затем мы могли бы сделать это с помощью плагина babel.

@SimenB Прежде всего, спасибо за отличную работу :)

На самом деле я сталкиваюсь с проблемой, когда хочу создать customEnvironment, расширенную с jest-environment-node . Мне нужно импортировать туда мою серверную реализацию, которая написана как esm. Но похоже, что среда должна быть определена как cjs .
У меня вопрос: есть ли возможность определить custom testEnvironment как esm, чтобы иметь возможность импортировать мой серверный модуль? Спасибо за любой совет.

@ kuka-radovan не могли бы вы создать для этого отдельный запрос функции?

ОБНОВЛЕНИЕ: эта проблема теперь отслеживается в https://github.com/facebook/jest/issues/10025

@SimenB Спасибо за jest.mock выше. Так получилось, что сегодня я конвертирую несколько файлов, которые в этом нуждаются. Я могу подтвердить, что ваш пример работает, когда имитируемый модуль представляет собой пакет node_modules , но он не работает для меня, имитирующего модуль в том же проекте.

Вот простой пример:

// main.js
import secondary from "./secondary.js";

export default function main() {
  return secondary();
}

// secondary.js
export default function secondary() {
  return true;
}

// test.js
import { jest } from "@jest/globals";

jest.mock("./secondary.js");

let main;
let secondary;
beforeAll(async () => {
  ({ default: main } = await import("./main.js"));
  ({ default: secondary } = await import("./secondary.js"));
});

test("works", () => {
  secondary.mockReturnValueOnce(false); // TypeError: Cannot read property 'mockReturnValueOnce' of undefined
  expect(main()).toBe(false);
});

Точно такой же шаблон работает, когда вместо имени пакета "./secondary.js" . (Я думаю, что пакет, который я пробовал с экспортом CommonJS, если это имеет значение.)

Jest 26.0.1 с узлом 12.16.3

Есть идеи, или я должен отправить полностью отдельный выпуск?

РЕДАКТИРОВАТЬ: transform: {} в конфигурации, так что никакого Бабеля вообще

РЕДАКТИРОВАТЬ 2: это тоже не работает:

jest.mock("./secondary.js", () => ({
  default: jest.fn()
}));

Потрясающая работа над этим.

Однако, если я не делаю что-то не так, пока не представляется возможным использовать import() в тестовом файле CJS.

Я запускаю Jest с node --experimental-vm-modules node_modules/jest/bin/jest.js и имею testEnvironment: 'node', transform: {} в jest.config.js . Это на узле 14.2.0.

Выражение импорта вызывает ошибку:

TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]:
A dynamic import callback was not specified.

Это известное ограничение в настоящее время? Я вижу, что https://github.com/nodejs/node/pull/32985 теперь находится в Node 14.1.0.

Да, я еще не успел это реализовать. Я, наверное, приземлю его в эти выходные.

@aldeed не могли бы вы открыть отдельную тему? Мне нужно пройти и убедиться, что макеты являются частью решения, и ваш пример кажется хорошим тестовым примером 🙂

@SimenB Спасибо за быстрый ответ. Я буду следить, когда он приземлится.

Поскольку import в скриптах может быть отменено из-за регрессии (https://github.com/nodejs/node/issues/33166), давайте подождем, пока это не уляжется.

У меня возникают проблемы при попытке использовать это с тестовыми файлами .mjs . Если у меня есть __tests__/my-test.mjs , я получаю

$ yarn test
yarn run v1.22.4
$ node --experimental-vm-modules node_modules/jest/bin/jest.js
No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In C:\Users\Domenic\Dropbox\Programming\WIP\remember-to-eat
  1 file checked.
  testMatch: **/__tests__/**/*.[jt]s?(x), **/?(*.)+(spec|test).[tj]s?(x) - 0 matches
  testPathIgnorePatterns: \\node_modules\\ - 1 match
  testRegex:  - 0 matches
Pattern:  - 0 matches
error Command failed with exit code 1.

Если я добавлю

"testMatch": ["**/__tests__/**/*.mjs"]

в мой package.json, я получаю

$ yarn test
yarn run v1.22.4
$ node --experimental-vm-modules node_modules/jest/bin/jest.js
No tests found, exiting with code 1
Run with `--passWithNoTests` to exit with code 0
In C:\Users\Domenic\Dropbox\Programming\WIP\remember-to-eat
  1 file checked.
  testMatch: **/__tests__/**/*.mjs - 0 matches
  testPathIgnorePatterns: \\node_modules\\ - 1 match
  testRegex:  - 0 matches
Pattern:  - 0 matches
error Command failed with exit code 1.

Однако, если я удалю "testMatch" а затем переименую свой файл в __tests__/my-test.js , это сработает.

Я хотел бы иметь возможность постоянно использовать расширения .mjs в своем проекте. Возможно ли это с Jest?

@domenic Я тоже столкнулся с этим. Решение - добавить в конфигурацию "moduleFileExtensions": ["js", "mjs"] (в дополнение к "testMatch" ).

Посмотрел, а moduleFileExtensions действительно надо.

Jest получает список всех файлов в проекте, выполнив здесь hasteFS.getAllFiles() :

https://github.com/facebook/jest/blob/2460c059ad1dbf124466ac25c8d5ccfd74ae9f25/packages/jest-core/src/SearchSource.ts#L159 -L164

hasteFS создается как часть HasteMap со следующей конфигурацией extensions :

https://github.com/facebook/jest/blob/2460c059ad1dbf124466ac25c8d5ccfd74ae9f25/packages/jest-runtime/src/index.ts#L291


Однако я не думаю, что в этом случае необходимо указывать moduleFileExtensions . Мы уже принудительно выполняем поиск .snap , должны ли мы также принудительно использовать известные расширения JS? Это (вне моей головы) js , mjs , cjs , jsx , ts и tsx ? Это замедлит сканирование, но я не думаю, что это окажет большое влияние. Я могу ошибаться? По умолчанию он не должен быть намного медленнее, поскольку только cjs и mjs уже не являются частью шаблонов по умолчанию, но для людей, у которых есть пользовательские шаблоны, это может замедлить работу?

Да, было бы идеально, если бы, по крайней мере, в режиме модулей ES .mjs просто работал, без необходимости добавлять moduleFileExtensions или изменять testMatch по умолчанию.

Также было бы неплохо, если бы я мог исключить файлы .js; когда я попробовал это я получил

 Validation Error:

  moduleFileExtensions must include 'js':
  but instead received:
    ["mjs"]
  Please change your configuration to include 'js'.

Мне интересно, имеет ли смысл иметь какой-то «режим ESM», который добавлял бы расширения файлов esm узла, а также помогал бы с компиляцией в js с использованием esm для выбора (# 9860).


Без js некоторые вещи, которые мы загружаем в песочницу, внутренне ломаются (поскольку в ней используется та же реализация require т. Д.). Вероятно, нам следует исправить это, чтобы пользователь не смог нас сломать.

Что касается замедления, оно уже довольно медленно для больших проектов, но я не знаю, насколько сильно влияет количество расширений. Но я согласен, что mjs и cjs должны быть добавлены по умолчанию. Указание moduleFileExtensions: ['js'] переопределит значения по умолчанию и ускорит его, не так ли? Так что, может быть, просто задокументируйте это как настройку производительности.

Спасибо за всю эту работу! Это конечно потрясающе. Я выполнил 3 шага ( "type": "module" в моем package.json, "testEnvironment": "jest-environment-node" в моей конфигурации jest и --experimental-vm-modules в интерфейсе командной строки), и, похоже, он тоже работает хорошо 🎉

Но затем я пытаюсь прочитать и использовать import.meta как описано в документации Node.js (и, судя по __dirname , но похоже, что import.meta не работает:

console.log(import.meta);

SyntaxError: [PATH]/files.test.js: Support for the experimental syntax 'importMeta' isn't currently enabled (31:20):
    Add @babel/plugin-syntax-import-meta (https://git.io/vbKK6) to the 'plugins' section of your Babel config to enable parsing.

У меня нет babel, и я думал, что babel с этой работой остался позади. Я вернусь, чтобы сообщить, смогу ли я как-то исправить это, не устанавливая babel.

Node.js v14.3.0, Jest v25.5.4

На данный момент я нашел обходной путь. Поскольку я запускаю сценарий из того же каталога, в котором находится мой файл в моей библиотеке , я могу просто сделать:

const __dirname = process.cwd();
const __filename = __dirname + "/files.test.js";

Я буду следить за этим репо на случай, если появятся какие-либо обновления, еще раз спасибо за это!

Вам необходимо явно отказаться от Babel, используя transform: {} качестве конфигурации

@SimenB Я могу подтвердить, что добавление transform: {} сработало, спасибо! Я неправильно понял этот момент как «не добавляйте преобразования», а не как «убирайте преобразования», как предполагалось.

Кстати, тестирование увеличилось с 2,4 секунды до 1,3 секунды, и они постоянно чувствуют себя быстрее.

Узел 12 был выпущен с непомеченным ESM (https://nodejs.org/en/blog/release/v12.17.0/). Однако, как отмечено в OP, использование Jest API _not_ не помечено

@SimenB Я ветку несколько раз, но все еще застрял (использую узел 12.17).

При запуске Jest 26.0.1 я получаю эту ошибку:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /app/tests/setup.js
require() of ES modules is not supported.
require() of /app/tests/setup.js from /app/node_modules/@jest/transform/build/ScriptTransformer.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename setup.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /app/package.json.

У меня transform: {}, и я работаю с node --experimental-vm-modules node_modules/jest/bin/jest.js .

Что мне не хватает?

@aldarund не уверен, не могли бы вы собрать минимальную репродукцию?

@SimenB вот минимальное репо для воспроизведения, просто запустите yarn test https://github.com/aledalgrande/jest-example - Я пробовал с Node 13/14, и результат тот же. Мне кажется, что поток глобальной настройки не обновлен для работы с ESM.

также вы упомянули кого-то другого 😆

~ Не @simenB , но @aledalgrande , похоже, у вас здесь все правильно из того, что я пробовал, посмотрите мой проект, полностью работающий на ESM, для сравнения (конфигурация jest в package.json ). ~

~ Чтобы отладить это, если возможно, я бы посоветовал упростить вашу конфигурацию шутки, чтобы _only_ имел два соответствующих свойства, возможно, даже вначале package.json . Затем добавьте все остальные свойства, которые вам сейчас нужны, чтобы увидеть, какое из них работает / не работает. ~

Во втором комментарии упоминаются globalSetup а не обычные тесты, тогда мой комментарий nvm. Если я удалю ключ globalSetup в Jest, тогда тест будет выполняться, как ожидалось в этом примере, но ключ globalSetup не работает, как вы сказали.

Ага, про глобальную настройку и разборку забыл. Можно исправить 👍

Привет @SimenB , снова я. Поддерживаются ли именованные экспорты? С помощью Node.js я могу импортировать и использовать такой пакет:

import { customAlphabet } from "nanoid";

Однако при попытке выполнить тест тот же код дает следующую ошибку:

SyntaxError: The requested module 'nanoid' does not provide an export named 'customAlphabet'

Для тестов я могу изменить код на этот, и он работает:

import nanoid from "nanoid";
const { customAlphabet } = nanoid;

Но затем версия Node.js перестает работать, поскольку на самом деле нет спорта по умолчанию (но по какой-то причине экспорт по умолчанию работает с Jest):

SyntaxError: The requested module 'nanoid' does not provide an export named 'default'

Опубликованный (репо, похоже, сейчас находится в движении) код nanoid заканчивается так, без экспорта по умолчанию:

export { nanoid, customAlphabet, customRandom, urlAlphabet, random }

Jest потребляет только «главную» точку входа. «экспорт» пока не рассматривается. Вы просто импортируете версию commonjs, которая имеет только экспорт по умолчанию.

А, я вижу, похоже, что package.json включает следующее:

  "main": "index.cjs",
  "module": "index.js",
  "exports": {
    "./package.json": "./package.json",
    ".": {
      "require": "./index.cjs",
      "import": "./index.js",
      "browser": "./index.browser.js"
    },
    ...
  }
  ...

Так что, вероятно, Node.js находит версию модуля, а Jest использует версию CommonJS, у которой нет именованного экспорта, верно?

Я подожду, пока Package Exports будет проверено, а затем протестирую его, еще раз спасибо за всю работу! Отметить эти 2 комментария как решенные до тех пор. Я имею в виду именно этот тест.

Я пересматриваю это, чтобы увидеть, как он работает - обновился до Jest 26.0.1 и узла 14.4. Установите package.json на тип модуля, установите transform на {} , env на jest-environment-node и запустите с node --experimental-vm-modules . Теперь я получаю эту новую ошибку:

ES Modules are only supported if your test environment has the `getVmContext` function

Мне не удалось найти информацию об этом, за исключением журнала изменений от Jest, в котором говорится, что getVmContext было добавлено некоторое время назад.

Любые идеи?

Не могли бы вы поделиться соответствующими частями вашего package.json пожалуйста, @cyberwombat ? Включая запускающий скрипт, который вы используете для Jest.

Для справки вот как это выглядит у меня на рабочем проекте :

{
  ...
  "type": "module",
  "scripts": {
    ...
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
  },
  "jest": {
    "transform": {},
    "testEnvironment": "jest-environment-node"
  },
  ...

Затем запустите его с помощью npm test

@franciscop У меня в принципе то же самое. Узел 14.4.0. Я могу нормально управлять твоим. Я углублюсь в детали, чтобы увидеть разницу.
package.json

{
  "type": "module",
  "devDependencies": {
    "jest": "^26.0.1",
  },
}

jest.config.js

export default {
  testEnvironment: 'jest-environment-node',
  setupFilesAfterEnv: ['./test/bootstrap.js'],
  testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/config/', '/<rootDir>/src/'],
  testRegex: '(\\.|/)(test|spec)\\.[jt]sx?$',
  transform: {
//    '^.+\\.jsx?$': 'babel-jest' // esm someday
  },
  transformIgnorePatterns: [],
  modulePaths: [
    '<rootDir>/test',
    '<rootDir>/src',
    '<rootDir>'
  ]
}

Сценарий:
node --experimental-vm-modules node_modules/jest/bin/jest.js

Не уверен, но я бы попытался сделать наоборот. Удалите все, кроме transform: {} и testEnvironment: 'jest-environment-node' , и начинайте добавлять каждый из параметров, пока не увидите, какой из них вызывает предыдущую ошибку. Я особенно подозреваю, что transformIgnorePatterns _может конфликтовать с transform , но я не очень знаком с параметрами шутки.

Всем привет! У меня возникла проблема при использовании Jest для тестирования приложения Express. Подробнее здесь . Не уверен, что это полезно для того, что вы делаете / отслеживаете здесь: roll_eyes:

@ x80486 Вчера я столкнулся с точно такой же проблемой . Я ответил в StackOverflow более подробным объяснением, насколько я понимаю.

Изменить: я не скрыл свой предыдущий комментарий, так как кажется, что он может быть актуальным, этот "exports" кажется популярным, скорее всего, из этой статьи о гибридных пакетах .

exports отслеживается в # 9771

Проблема @franciscop ok решена - оказывается, есть конфликт в пакетах - у меня установлен serverless-bundle что вызывает ошибку ES Modules are only supported if your test environment has the getVmContext function . Я не уверен, почему - я бы предположил, что его установка не вызовет конфликта с Jest, но, очевидно, это так.

@franciscop Я думаю, что причина, по которой проблемы, связанные с pkg.exports начинают появляться сейчас, заключается в том, что эта функция не была отмечена в Node.js 14.x и некоторые сопровождающие пакетов (например, я для uuid ) начали добавление полей pkg.exports . Итак, хотя вам нужен флаг командной строки для активации этой функции в Node.js 12.x , теперь вы получаете такое поведение по умолчанию.

На адаптацию всей экосистемы уйдет некоторое время, поэтому благодарим за сообщение о проблемах по этой теме!

Для тех, кто публикует сообщение о exports , на случай, если он был потерян в длинной ветке этой проблемы, в моем закрытом выпуске по этому поводу (https://github.com/facebook/jest/issues/9565) есть пример обходного пути moduleNameMapper в нем.

Проблема globalSetup о которой сообщалось в мае, вероятно, все еще существует (Jest 26.1.0)? Получение тех же ошибок, что и в примере репо @aledalgrande обеспечивает:

$ git clone [email protected]:aledalgrande/jest-example.git
$ cd jest-example
$ npm test

> @ test /Users/asko/Temp/jest-example
> node --experimental-vm-modules node_modules/jest/bin/jest.js --config=./jest.config.js

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/asko/Temp/jest-example/tests/setup.js
require() of ES modules is not supported.
require() of /Users/asko/Temp/jest-example/tests/setup.js from /Users/asko/Temp/jest-example/node_modules/@jest/transform/build/ScriptTransformer.js 

Без спешки. Проверено CHANGELOG и в нем не упоминается исправление globalSetup / globalTeardown с ES6.

Node.js 14.4.0, Jest 26.1.0


Обновление (13 августа 20):

По-прежнему невозможно, Node.js 14.7.0, Jest 26.4.0

Второстепенное мнение, но должен ли этот вопрос быть закрепленным вопросом, поскольку в настоящее время он является предметом шуток?

Есть мысли о том, что нужно сделать, чтобы использовать тестовые репортеры, написанные на модулях ES? ...
с последней версией jest я получаю ошибку, которая, по сути, говорит, что testScheduler ожидает настраиваемого репортера в формате commonjs.


чтобы увидеть ошибку

~ / проекты / esw-ts / lib / dist / test / testReporter.js: 1
импортировать os из 'os';
^^^^^^

SyntaxError: невозможно использовать оператор импорта вне модуля
в wrapSafe (внутренний / модули / cjs / loader.js: 1116: 16)
в Module._compile (внутренний / модули / cjs / loader.js: 1164: 27)
в Object.Module._extensions..js (internal / modules / cjs / loader.js: 1220: 10)
в Module.load (internal / modules / cjs / loader.js: 1049: 32)
в Function.Module._load (internal / modules / cjs / loader.js: 937: 14)
в Module.require (internal / modules / cjs / loader.js: 1089: 19)
при необходимости (внутренние / модули / cjs / helpers.js: 73: 18)
в /Users/manish.gowardipe/Desktop/projects/esw-ts/lib/node_modules/@jest/core/build/TestScheduler.js:418:65
в Array.forEach ()
в TestScheduler._addCustomReporters (/Users/manish.gowardipe/Desktop/projects/esw-ts/lib/node_modules/@jest/core/build/TestScheduler.js:411:15)

Привет, я хочу протестировать встроенную поддержку модулей ES в моем небольшом проекте, но я новичок в NodeJS, и я запутался в этом выпуске, мне бы хотелось получить некоторые рекомендации, пожалуйста.

  • node --version : v14.5.0
  • yarn jest --version : 26.1.0
  • Я пытаюсь протестировать этот небольшой проект , он очень простой.
  • У меня есть такие файлы:

package.json

{
"jest": {
    "transform": {},
    "testEnvironment": "jest-environment-node"
  }
}

markov.test.js

const fs = require("fs");
const Markov = require("./markov.mjs");
// import fs from "fs";
// import Markov from "./markov.mjs";

const file = fs.readFileSync("text.txt", "utf8");
const markov = new Markov(file.toString());

test("Generates sentence with especified words", () => {
  expect(markov.makeSentence(8).length).toBe(8);
});
  • Я запускаю yarn jest . и получаю такую ​​ошибку:
    imagen

  • Я пробовал с node node_modules/jest/bin/jest.js . и это дает мне ту же ошибку.

@ pepetorres1998 Этот поток посвящен запуску Jest с собственными модулями esm, который включает запуск вещей с определенными флагами / параметрами - см. комментарий выше, чтобы узнать, что делать (и установите "type": "module" в package.json). Честно говоря, на данный момент он не совсем готов для прайм-тайма, поэтому, если вам нужен ваш проект для работы, я могу остановиться на Babel. Есть ряд неотмеченных проблем, которые действительно мешают шоу. Я радостно попытался переключиться пару недель назад и вернулся в Вавилон с слезами.

Кто-нибудь еще получает ReferenceError: jest is not defined при попытке сделать что-то вроде jest.setTimeout(...) в тестовом файле с этой настройкой? Попытка выяснить, связано ли это со средой модуля es, версией узла, версией jest или какой-либо комбинацией этих вещей. (В настоящее время используется узел v14.5.0, jest 26.1.0, среда jest-environment-node)

РЕДАКТИРОВАТЬ
Теперь я вижу снятый флажок в описании проблемы для шутливого «глобального» свойства. 🙃

@bdentino Я думаю, вы можете попробовать явно импортировать его import {jest} from '@jest/globals';

25.4.0 был выпущен с первыми частями поддержки. В дополнение к # 9772, упомянутому выше, я также включил # 9842. В _theory_ смешивание CJS и ESM теперь должно работать правильно (🤞).

Единственная основная недостающая функция - поддержка объекта jest . Я не решил, следует ли нам придерживаться import.meta или требовать, чтобы люди импортировали его через import {jest} from '@jest/globals' . Обратная связь приветствуется!

Я еще не написал для этого документацию, но для ее активации вам нужно сделать 3 вещи

  1. убедитесь, что вы не запускаете преобразование из операторов import (установите transform: {} в конфигурации или иным образом убедитесь, что babel не преобразует файл в CJS, например, избегая modules вариант для preset-env)
  2. Запустить node@^12.16.0 || >=13.2.0 с флагом --experimental-vm-modules
  3. Запустите тест с jest-environment-node или jest-environment-jsdom-sixteen

Пожалуйста, попробуйте и оставьте отзыв! Если вы сообщаете об ошибках, было бы замечательно, если бы вы также могли указать, как запуск того же кода (за вычетом любого кода, специфичного для теста) выполняется в Node. Я читал https://nodejs.org/api/esm.html _a lot_ за последние несколько недель, но, вероятно, что-то упустил.

@SimenB
Эта тема стала огромной, и я думаю, что тем, кто хочет начать с шутки / использования модулей ES, будут трудности с поиском и пониманием основных рекомендаций, чтобы начать это делать.
Есть ли в документации формальное объяснение добавления шутки в проект ES-модулей (или какое-то «быстрое начало»)?

@aldeed Что касается вашей проблемы с

(Кстати, мы также используем responsecommerce, так что приветствую это, ха-ха)

@guilhermetelles нет, и теперь это отслеживается на https://github.com/facebook/jest/issues/10025 .

Я использую Jest 26.1.0, node версии 14.6.0 с --experimental-vm-modules , но я все еще вижу ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING при использовании import() внутри CommonJS . Стоит ли попробовать придумать минимальное воспроизведение и открыть новый выпуск?

Кстати, есть ли простой способ добавить yarn link копию пакетов jest в проект теперь, когда Jest использует yarn Berry? Я хотел попробовать последнюю версию master всякий случай, если она еще не выпущена. Я пытался сделать что-то вроде path/to/facebook/jest/.yarn/releases/yarn-sources.cjs link --all path/to/jest , но ничего не вышло. Запуск чего-то вроде cd node_modules; for p in jest*; do if [[ -d path/to/jest/packages/$p ]]; then rm -rf $p; ln -s path/to/jest/packages/$p; fi; done вручную тоже не работал, я не знаю почему.

@vvanpo import() в CJS был отменен в Node, вы можете следить за https://github.com/nodejs/node/issues/31860

Что касается локального запуска, я обычно просто удаляю jest из проекта, который хочу протестировать, и выполняю ../jest/jest . Потенциально nose ../jest/packages/jest/bin/jest.js . Просто не забудьте сначала запустить yarn и yarn build:js . Если эти инструкции не работают (я пишу по памяти на телефоне в самолете), пожалуйста, сообщите о проблеме (или PR), чтобы мы могли правильно записать это в файл CONTRIBUTING.md

Планируете ли вы поддерживать циклический импорт?

Если у меня есть фиктивный тестовый файл, который импортирует только один из двух файлов, которые импортируют только друг друга, я получаю RangeError: Maximum call stack size exceeded . Если я удалю один из импортов, тест пройден. Репо, воспроизводящее проблему .

Привет! Я установил это в проекте с пустым узлом, и он работал очень хорошо, однако в наших производственных настройках я получаю следующее сообщение об ошибке, когда пытаюсь запустить тесты:

ES Modules are only supported if your test environment has the 'getVmContext' function

Я видел, что у кого-то еще возникла проблема в более раннем ответе (от @cyberwombat ), но пакет, который они нашли виновным, отсутствует в нашем файле package.json . Как определить пакет (или параметр), вызывающий проблему? Я пытался систематически удалять все настройки шутки, которые не нужны для работы, но безуспешно.

ОБНОВЛЕНИЕ : мне удалось добиться прогресса, немного изменив jest-runtime . Я остановил отладчик в строке, которая пытается получить доступ к контексту виртуальной машины, и хотя функция действительно не существует, существует this.context (которую она должна вернуть), поэтому я изменил эту строку для прямого доступа к свойству. Я знаю, что это, вероятно, не идеально, но, может быть, @SimenB может дать вам представление о том, что происходит не так?

Спасибо заранее за любую помощь

Планируете ли вы поддерживать циклический импорт?

Определенно! Не могли бы вы поднять отдельный вопрос?


@zsombro похоже, что вы используете старую версию тестовой среды. Если вы запустите jest --show-config , что отобразит testEnvironment ?

похоже, вы используете старую версию тестовой среды. Если вы запустите jest --show-config , что отобразит testEnvironment ?

@SimenB говорит следующее:

"testEnvironment": "/Users/zberki/git/project-name/node_modules/jest-environment-node/build/index.js",
"testEnvironmentOptions": {},

Я просто установил его на jest-environment-node с вашими инструкциями

Прежде чем я начал этот процесс, я обновил jest, используя yarn add jest@latest . Нужно ли обновлять среду отдельно?

ОБНОВЛЕНИЕ: Оказывается, пришлось. Я удалил node_modules и yarn.lock чтобы выполнить чистую установку, но это все равно не сработало. Однако, если я добавлю его вручную с помощью yarn add -D jest-environment-node похоже, это сработает. Есть ли лучший способ справиться с этим? Я сделал минималистичный тестовый проект перед тем, как сделать это на нашей кодовой базе, и мне не нужно было этого делать

yarn list jest-environemnt-node (или npm list jest-environemnt-node ), вероятно, будет перечислять несколько, я предполагаю

├─ [email protected]
│  └─ [email protected]
└─ [email protected]

версия 26.2.0 вероятно, была установлена ​​мной вручную (по крайней мере, на основе package.json , что означает, что jest-config установил версию, которая явно устарела?

У вас есть что-то еще, использующее более старую версию jest-config (возможно, react-scripts (часть create-react-app )?). Этот вопрос не место для его обсуждения, хотя 🙂

Неспособность использовать модули ES в моем globalSetup начинает мешать.

Два момента:

  • следует ли это указать в качестве флажка в начале этого вопроса (чтобы он отслеживался)?
  • если есть альфа / бета, я могу попробовать, готов сделать это

Я:

  • Убедитесь, что я использую новейшую версию Jest (26.4.0)
  • Добавлен узел jest-environment-node в мой проект
  • Убедитесь, что он не дублируется, проверив файл блокировки
  • Добавлен "testEnvironment": "jest-environment-node", в jest.config.json
  • Добавлен import { jest } from '@jest/globals'; везде, где использовалась шутка
  • Выполните тестовую настройку команды --experimental-vm-modules , запустив их с помощью NODE_OPTIONS='--experimental-vm-modules' yarn jest

И он вылетает в следующем коде:

jest.mock('../../some/other/path', () => ({
  someOtherMethod: jest.fn().mockImplementation(…),
}));

со следующей ошибкой (сокращенно - обратите внимание на «Разрешенные объекты»!):

ReferenceError: src/foo/bar.spec.js: The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
Invalid variable access: jest
Allowed objects: Array, …, jest, …, unescape.
Note: This is a precaution to guard against uninitialized mock variables. If it is ensured that the mock is required lazily, variable names prefixed with `mock` (case insensitive) are permitted.

Я не могу использовать Babel, потому что он неправильно анализирует импорт, который я исправил для запуска на Node 14 без Babel:

-import { map } from 'lodash';
+import lodash from 'lodash';
+const { map } = lodash;

Что, к сожалению, неправильно анализируется @babel/preset-env , что приводит к TypeError: Cannot destructure property 'map' of '_lodash.default' as it is undefined. .

Может ли кто-нибудь помочь мне обойти эту проблему?

Изменить: похоже, вы _ можете_ использовать Jest + Babel в коде, совместимом с собственными модулями ES, используя импорт CommonJS, выполнив это абсолютно отвратительное исправление:

jest.mock('common-js-module', () => ({
  __esModule: false,
  ...jest.requireActual('common-js-module'),
}));

Сюда,

import lodash from 'lodash';
const { map } = lodash;

отлично используется Node 14, а код, полученный в результате запуска Jest + Babel,

var _lodash = _interopRequireDefault(require("lodash"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const {
  map
} = _lodash.default;

тоже работает.

мы успешно преобразовали все наши шутливые тесты для использования и импорта нашего кода ES6, но мы застряли на нескольких пакетах: а именно puppeteer и uuid

Приложение работает, только если мы импортируем их в объект (например, import uuid from 'uuid' ), но тесты не будут запускаться таким образом. Однако, если мы заменим этот импорт синтаксисом деконструкции (например, import { v4 } from 'uuid' , тогда все будет наоборот: тест работает, но приложение выдает исключение.

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

Однако, если мы заменим этот импорт синтаксисом деконструкции (например, import {v4} from 'uuid', тогда все будет наоборот: тест работает, но приложение выдает исключение.

Похоже, ваше приложение скомпилировано в CommonJS и на практике не использует модули. Из «настоящего» ESM import uuid from 'uuid' не должно работать, потому что uuid не имеет экспорта по умолчанию и предоставляет сборку ESM для узла .

Привет, @SimenB , как ты думаешь, было бы неплохо получить предварительную документацию по этому

@grantcarthew определенно! Я надеялся, что смогу уделить этому больше времени и стабилизировать его для Jest 27, но сомневаюсь, что смогу это сделать. Но написать страницу с документами о том, что там сейчас (и что это экспериментально), кажется хорошей идеей.

@SimenB Я не знаю, каков текущий статус проблемы и должен ли Jest уже работать с моим делом или нет, но, возможно, это может вам как-то помочь.

Я пытаюсь загрузить библиотеку только для esm (их расширение - cjs, но тип - модуль, и узел, похоже, с этим согласен), но Jest не может правильно загрузить ее с ошибкой:

    C:\dev\codemirror-next-repro-cra\test-in-jest-esm\node_modules\style-mod\dist\style-mod.cjs:15
    export var StyleModule = function StyleModule(spec, options) {

Здесь вопрос, который я изначально открыл, https://github.com/codemirror/codemirror.next/issues/310. И репродукция ошибки Jest + ESM с узлом 14.13.1 https://github.com/dubzzz/codemirror-next-repro-cra/tree/main/test-in-jest-esm

@dubzzz у вас не может быть ESM в файле cjs . Узел тоже не работает

$ node node_modules/style-mod/dist/style-mod.cjs
(node:48829) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/simen/repos/codemirror-next-repro-cra/test-in-jest-esm/node_modules/style-mod/dist/style-mod.cjs:15
export var StyleModule = function StyleModule(spec, options) {
^^^^^^

SyntaxError: Unexpected token 'export'
    at wrapSafe (internal/modules/cjs/loader.js:1172:16)
    at Module._compile (internal/modules/cjs/loader.js:1220:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1277:10)
    at Module.load (internal/modules/cjs/loader.js:1105:32)
    at Function.Module._load (internal/modules/cjs/loader.js:967:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)
    at internal/main/run_main_module.js:17:47

Упс, извините, я слишком быстро попытался на стороне узла. @ nicolo-ribaudo уже уведомил автора библиотеки об этой проблеме.
Большое спасибо за быстрый ответ.

Я открыл PR для некоторых (почти незавершенных) документов здесь: # 10611. Я не стал перечислять недостающие функции / ошибки, так как думаю, что их будет сложно синхронизировать с реальностью, а рассмотрение проблем с github - лучший подход, поскольку они (надеюсь…) обновлены.

@Pomax в качестве нового выпуска, пожалуйста 🙂

Я только что открыл # 10620, который добавляет поддержку import() от CJS. Запрошено несколько раз, например https://github.com/facebook/jest/issues/9430#issuecomment -626054595

Привет. Мне довольно сложно быстро охватить всю историю ESM в node / jest, поэтому, вероятно, я спрашиваю что-то очевидное или уже ответил. Правильно ли я понимаю, что следующий случай еще не поддерживается? Или, надеюсь, я что-то не так делаю? Мне кажется, что import x from 'x' работает, а деструктуризация import { sub } from 'x' - нет.

package.json:

{
  "name": "jest-uuid",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "test": "node --experimental-vm-modules node_modules/.bin/jest"
  },
  "devDependencies": {
    "jest": "26.5.2"
  },
  "dependencies": {
    "uuid": "8.3.1"
  }
}

f.spec.js

import { v4 } from 'uuid';
test('', () => {});

тест npm

> npm test

> [email protected] test /Users/igoro/p/tmp/jest-uuid
> node --experimental-vm-modules node_modules/.bin/jest

 FAIL  ./f.spec.js
  ● Test suite failed to run

    SyntaxError: The requested module 'uuid' does not provide an export named 'v4'

      at jasmine2 (node_modules/jest-jasmine2/build/index.js:228:5)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.879 s
Ran all test suites.
(node:94492) ExperimentalWarning: VM Modules is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
npm ERR! Test failed.  See above for more details.

Вы ждете # 9771. До этого Jest не знал, что загружать uuid как ESM безопасно (или, скорее, какой файл загружать, в какой момент он узнает, что это ESM)

Будет ли это соответствовать собственному соглашению Node, где CJS может быть загружен только как пространство имен, или это «улучшит» это, разрешив синтаксис, который на самом деле не работает в самом Node? (например, Node не позволяет import { readdirSync } from "fs-extra" потому что это пакет CJS, но разрешает import fs from "fs-extra"; который затем можно распаковать с помощью const { readdirSync } = fs ).

(например, Node не позволяет импортировать {spawn} из "child_process", потому что это пакет CJS, но позволяет импортировать child_process из "child_process"; который затем можно распаковать с помощью const {spawn} = child_process;).

Это неудачный пример, потому что узел считает «child_process» «встроенным» (а не CJS) модулем, поэтому так называемый экспорт действительно работает. В последних версиях nodejs также используется эвристика, чтобы заставить многие именованные экспорты работать с модулями CJS. Это может быть труднее всего подражать.

обновлен пример: вместо него используется fs-extra . Но если именованный экспорт входит в дорожную карту Node для достижения этого или следующего крупного проекта, то вытеснение Jest имеет смысл.

Это уже должно быть реализовано - модули ядра узла предоставляют именованный экспорт, а «нормальный» CJS - нет.

В последних версиях nodejs также используется эвристика, чтобы заставить многие именованные экспорты работать с модулями CJS. Это может быть труднее всего подражать.

У вас есть ссылка на реализующий PR? Мы должны попытаться хотя бы подражать этому 🙂

PR здесь: https://github.com/nodejs/node/pull/35249

Эвристика, лежащая в основе этого, опубликована как cjs-module-lexer (https://github.com/guybedford/cjs-module-lexer), но @guybedford может знать больше о любых потенциальных отклонениях.

Только что взглянул на это, и похоже, что fs-extra использует шаблон экспорта, например:

module.exports = {
  // Export promiseified graceful-fs:
  ...require('./fs'),
  // Export extra methods:
  ...require('./copy-sync'),
  ...require('./copy'),
  ...require('./empty'),
  ...require('./ensure'),
  ...require('./json'),
  ...require('./mkdirs'),
  ...require('./move-sync'),
  ...require('./move'),
  ...require('./output'),
  ...require('./path-exists'),
  ...require('./remove')
}

В настоящее время это не тот случай анализа реэкспорта, который мы обнаруживаем, но его можно было бы добавить в cjs-module-lexer, если бы это было полезно для обработки обнаружения именованного экспорта.

Спасибо @jkrems и @guybedford! Я открыл PR, используя этот модуль: # 10673

Точная поддержка fs-extra, описанная в https://github.com/facebook/jest/issues/9430#issuecomment -713204282, теперь реализована в [email protected] , отслеживание восходящего потока на https: // github. com / nodejs / узел / pull / 35745.

_Update: тестирование этой сборки, оно правильно определяет все функции fs-extra, но, к сожалению, не обнаруживает собственные функции Node.js, поскольку они не поддаются статическому анализу из-за заполнения циклом for._

feat: поддержка именованного экспорта из CJS как именованного импорта ESM # 10673

Я думал, что собственный ESM поддерживает только импорт exports модуля CommonJS как default ?

@trusktr больше нет: https://github.com/nodejs/node/pull/35249

Привет. Мне довольно сложно быстро охватить всю историю ESM в node / jest, поэтому, вероятно, я спрашиваю что-то очевидное или уже ответил. Правильно ли я понимаю, что следующий случай еще не поддерживается? Или, надеюсь, я что-то не так делаю? Мне кажется, что import x from 'x' работает, а деструктуризация import { sub } from 'x' - нет.

...
импортировать {v4} из uuid;

Модули ESM не поддерживают импорт с деструктуризацией, хотя синтаксис выглядит так, как есть. Чтобы это работало, необходим экспорт v4. "экспорт по умолчанию" не подходит.

https://kentcdodds.com/blog/misunderstanding-es6-modules-upgrading-babel-tears-and-a-solution

@sdwlig uuid предоставляет именованный экспорт и не имеет значения по умолчанию. Он должен работать, но загрузка esm из пакетов с полем «экспорт» пока не поддерживается шуткой. Вместо этого загружается Commonjs, который доступен только через экспорт по умолчанию.
https://github.com/uuidjs/uuid/blob/master/src/index.js

Можем ли мы добавить к этому поддержку самостоятельных ссылок на пакеты (# 10883)?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги