Когда я пытаюсь запустить Cucumber.js с определением шага, определенным в собственном модуле ECMAScript (как описано здесь ), попытка завершается ошибкой с предупреждением о том, что я использую CommonJS require()
в модуле ES.
Версия Cucumber.js: 6.0.5
Версия узла: 13.8.0
npm init
и npm i cucumber
"type": "module"
в файле package.json
чтобы файлы JS обрабатывались как собственные модули.Создайте базовый файл функций features/mjs.feature
:
Feature: Native JS Modules
Scenario: Load a native JS module step definition
Given I have 42 cucumbers in my belly
Создайте базовый файл определения шага features/step_definitions.js
:
import { Given } from "cucumber";
Given("I have {int} cucumbers in my belly", function (cucumberCount) {
console.log("Step parsed.");
});
Попытка запустить Cucumber:
$ ./node_modules/.bin/cucumber-js
Модуль определения шага должен быть загружен и использоваться для анализа шага.
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /some/path/cucumbertest/features/step_definitions.js
require() of ES modules is not supported.
require() of /some/path/cucumbertest/features/step_definitions.js from /some/path/cucumbertest/node_modules/cucumber/lib/cli/index.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 step_definitions.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /some/path/cucumbertest/package.json.
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:13)
at Module.load (internal/modules/cjs/loader.js:1000:32)
at Function.Module._load (internal/modules/cjs/loader.js:899:14)
at Module.require (internal/modules/cjs/loader.js:1040:19)
at require (internal/modules/cjs/helpers.js:72:18)
at /some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js:119:42
at Array.forEach (<anonymous>)
at Cli.getSupportCodeLibrary (/some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js:119:22)
at Cli.run (/some/path/cucumbertest/node_modules/cucumber/lib/cli/index.js:141:37)
at async Object.run [as default] (/some/path/cucumbertest/node_modules/cucumber/lib/cli/run.js:30:14)
Как бы то ни было, я очень ценю работу, которую команда Cucumber.js проделала над этим проектом, это был большой актив для меня и моей компании. Моя команда потратила некоторое время на создание некоторых общих компонентов приложения в собственных модулях JS, и до недавнего времени я предполагал (на основе синтаксиса import
в самой последней документации), что этот материал будет работать с Cucumber, когда мы добрался до интеграции с нашей тестовой средой. Однако, похоже, это не так. Поскольку общий компонент уже написан и интегрирован в другие части нашего приложения, я не хочу использовать только определения шагов и код поддержки, написанный на CommonJS, из-за проблем с совместимостью.
Другие вещи, которые я пытался заставить это работать, включены ...
.mjs
соответствии с соглашением Node.js (это привело к тому, что логика автоматической загрузки Cucumber не обратила на них внимания, а использование --require
для явной загрузки модуля определения шага привело к Error [ERR_REQUIRE_ESM]: Must use import to load ES Module
import()
; проблема заключалась в том, что этот подход является асинхронным, и даже если обещание импорта экспортируется модулем CJS, Cucumber, похоже, не ожидает разрешения этого обещания, прежде чем продолжить, потому что шаг был помечен как неопределенный (предполагая, что определение шага в родном модуле еще не прописался).Если это в моих силах, я был бы рад отправить PR, чтобы помочь решить эту проблему, но я не знаком с внутренней работой Cucumber.js и был бы признателен, если бы кто-нибудь мог указать мне в правильном направлении.
Я бы хотел, чтобы cucumber-js работал с ESM. У нас уже были открыты некоторые другие вопросы по этому поводу.
Мы могли бы создать параметр CLI (или использовать другой способ поиска, когда его использовать), который заставляет cucumber-js использовать import
вместо require и ждать возвращенного обещания.
Учитывая добавленный вами пример, мы могли бы добавить тестовый пример специально для этого и работать над тем, чтобы он прошел. Экспериментируя с переключением, требующим импорта и ожидания обещания, я столкнулся с другой ошибкой:
⋊> ~/p/test ./node_modules/.bin/cucumber-js 21:05:22
(node:7422) ExperimentalWarning: The ESM module loader is experimental.
file:///test/features/steps.js:1
import { Given } from "cucumber";
^^^^^
SyntaxError: The requested module 'cucumber' does not provide an export named 'Given'
at ModuleJob._instantiate (internal/modules/esm/module_job.js:92:21)
at async ModuleJob.run (internal/modules/esm/module_job.js:107:20)
at async Loader.import (internal/modules/esm/loader.js:176:24)
at async Promise.all (index 0)
at async Cli.getSupportCodeLibrary (/test/node_modules/cucumber/lib/cli/index.js:119:5)
at async Cli.run (/test/node_modules/cucumber/lib/cli/index.js:141:32)
at async Object.run [as default] (/test/node_modules/cucumber/lib/cli/run.js:30:14)
Я взял пример (слегка измененный) из документации по определению шага, но вы правы, он не будет работать так, как написано. Поскольку Cucumber.js является модулем CommonJS, он предоставляет только экспорт по умолчанию. В краткосрочной перспективе я, вероятно, просто импортирую модуль по умолчанию, а затем получу доступ к методам Given / When / Then из этого:
import cucumber from "cucumber";
cucumber.Given(...);
В долгосрочной перспективе было бы неплохо, если бы у Cucumber были отдельные точки входа для импорта и требования, как показано здесь .
Спасибо, что посмотрели на это. Как бы то ни было, вариант интерфейса командной строки, указывающий Cucumber на импорт, а не на требование, меня вполне устроил. Также было бы замечательно, если бы Cucumber искал файлы поддержки с расширениями .cjs и .mjs и автоматически рассматривал их как CommonJS и собственные модули соответственно.
РЕДАКТИРОВАТЬ: просто хочу повторить, что я рад помочь с любым из этого, если могу; просто укажите мне правильное направление.
Я бы, вероятно, просто импортировал модуль по умолчанию, а затем получил бы доступ к методам Given / When / Then из этого:
импортный огурец из "огурца";
огурец.Данный (...);
Это не работает. Я получаю то же сообщение.
У меня есть "type": "module" в package.json, а остальная часть моего проекта использует модули, поэтому изменение этого варианта недопустимо.
Если я использую одно из них:
import cucumber from '@cucumber/cucumber';
// OR
import { When } from '@cucumber/cucumber';
Я получаю то же сообщение об ошибке, что и исходное сообщение.
Если я перейду на
const cucumber = require('@cucumber/cucumber');
Затем я получаю это сообщение:
Вместо этого переименуйте /some/path/cucumbertest/features/step_definitions.js так, чтобы он заканчивался на .cjs, измените требуемый код для использования import () или удалите "type": "module" из project / package.json.
Если я переименую step_definitions.js
в _either_ step_definitions.cjs
или step_definitions.mjs
, он просто игнорируется, и я получаю общие сообщения от огурца, в которых говорится о создании заглушек для моих шагов:
UUUUUU
Failures:
1) Scenario: Empty Payload # spec\cucumber\features\users\create\main.feature:4
? When the client creates a POST request to /users
Undefined. Implement with the following snippet:
When('the client creates a POST request to \/users', function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
... etc
Я получаю точно такие же сообщения, если полностью удаляю файл, поэтому он явно игнорируется.
В конце концов, единственный способ заставить огурец запускаться в проекте с "type": "module" - это создать отдельный specpackage.json без "type": "module" и установить огурец в spec/node_modules
.
Пока вроде работает. Посмотрим, работает ли он по-прежнему, когда я создам несколько реальных тестов.
Сопровождающим: я впервые использую огурец, и пока мне это нравится. Однако это действительно помешало моему прогрессу. Вместо того, чтобы тратить полдня на изучение огурца, я потратил его на отладку импорта. Я знаю, как досадно пытаться создать библиотеку, поддерживающую как импорт ES6, так и CJS, так что я вам сочувствую.
Возможное решение:
Можно опубликовать сборки CJS и ES6 одновременно, если вы создадите второй файл dist:
dist / cucumber.js
dist / cucumber.module.js
Затем добавьте эту строку в package.json:
"модуль": "dist / cucumber.module.js"
Это должно позволить узлу разрешить импорт ES6.
должен быть лучший способ сделать это. У меня тоже есть эта проблема, и поскольку импорт не может использоваться с commonJS, мне пришлось бы использовать
https://nodejs.org/api/esm.html#esm_import_expressions
где угодно...
есть ли способ, которым Cucumber может иметь флаг, который помогает нам использовать ES?
Быстрый для людей, заинтересованных в этом: когда вы используете ESM в проекте с огурцом, вы бы:
.js
и "type":"module"
.mjs
Спасибо!
Я стараюсь следовать соглашению об использовании расширений .mjs и .cjs для всех моих файлов JavaScript, чтобы избежать двусмысленности, и я бы предпочел сделать это и в этом случае. Однако не будет большим неудобством установить «type»: «module» на уровне проекта и оставить их как файлы .js, если это лучшее решение для большего числа пользователей.
Я использую расширение .js
и "type":"module"
так как ожидаю, что это станет нормой в долгосрочной перспективе.
Спасибо за отзыв @adamlacoste @looeee
@davidjgoss достаточно ли
Спасибо за подталкивающий @ aurelien-reeves!
Версия 7.2.0 @cucumber/cucumber
теперь находится на npm, включая экспериментальную поддержку ESM, как описано в документации здесь:
https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#es-modules-experimental-nodejs-12
Если бы заинтересованные могли попробовать и сообщить, что вы нашли, это было бы здорово. Я разместил здесь суперминимальный пример проекта:
https://github.com/davidjgoss/cucumber-esm-example
Мы рады оставить этот вопрос открытым ненадолго, просто чтобы собрать ранние отзывы в одном месте.
Таким образом, это изменение вызвало проблему со сторонними фреймворками и программами форматирования, которые напрямую (а не из точки входа) использовали require
определенные вещи. Я выпустил 7.2.1, который в основном представляет собой возврат к 7.1.0, чтобы разблокировать этих людей, и я раскрою причину и посмотрю, сможем ли мы ее избежать, продолжая поддерживать ESM. А пока, если это вас не коснется, можно поэкспериментировать с 7.2.0.
я пытался дать v7.2.0
шанс вместе с флагом --esm
. Я использую testdouble в тестах для пакета, который я пытаюсь преобразовать в ESM.
чтобы использовать td.replaceEsm
, необходимо использовать загрузчик, например --loader=testdouble
. когда я пытаюсь предоставить загрузчик непосредственно в cli огурца, я получаю следующую ошибку:
> cucumber-js test/integration --esm --loader=testdouble
error: unknown option '--loader=testdouble'
поскольку эта опция была недоступна, я попытался использовать NODE_OPTIONS
:
> NODE_OPTIONS='--loader=testdouble' cucumber-js test/integration --esm
(node:62231) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
internal/process/esm_loader.js:74
internalBinding('errors').triggerUncaughtException(
^
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension "" for /path/to/project/node_modules/@cucumber/cucumber/bin/cucumber-js
at defaultGetFormat (internal/modules/esm/get_format.js:71:15)
at getFormat (file:///path/to/project/node_modules/quibble/lib/quibble.mjs:65:12)
at Loader.getFormat (internal/modules/esm/loader.js:104:42)
at Loader.getModuleJob (internal/modules/esm/loader.js:242:31)
at async Loader.import (internal/modules/esm/loader.js:176:17)
at async Object.loadESM (internal/process/esm_loader.js:68:5) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
для справки, это было против узла v14.17.0
Было бы неплохо, если бы это работало с подходом NODE_OPTIONS
, но в идеале опция --loader
будет поддерживаться непосредственно cli, как некоторые тестовые фреймворки начали поддерживать. пожалуйста, рассмотрите эти подходы со своей следующей версией, которая приближается к поддержке ESM :)
есть ли подход к определению загрузчика, который, как вы ожидаете, будет работать с v7.2.0
?
Самый полезный комментарий
Я использую расширение
.js
и"type":"module"
так как ожидаю, что это станет нормой в долгосрочной перспективе.