Cucumber-js: 步骤定义和支持代码中的原生 JS 模块支持

创建于 2020-04-03  ·  13评论  ·  资料来源: cucumber/cucumber-js

问题

当我尝试使用在本机 ECMAScript 模块中定义的步骤定义运行 Cucumber.js 时(如此处所述),尝试失败,并显示我在 ES 模块上使用 CommonJS 的require()的警告。

Cucumber.js 版本: 6.0.5
节点版本: 13.8.0

重现步骤

  1. 使用npm initnpm i cucumber设置基本的 NPM 包目录

    • package.json文件中设置"type": "module"以确保 JS 文件将被视为原生模块

  2. 创建一个基本的特征文件, features/mjs.feature

    Feature: Native JS Modules
    
        Scenario: Load a native JS module step definition
            Given I have 42 cucumbers in my belly
    
  3. 创建一个基本的步骤定义文件, features/step_definitions.js

    import { Given } from "cucumber";
    
    Given("I have {int} cucumbers in my belly", function (cucumberCount) {
        console.log("Step parsed.");
    });
    
  4. 尝试运行 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语法)当我们开始与我们的测试框架集成。 不过,情况似乎并非如此。 由于共享组件已经编写并集成到我们应用程序的其他部分,由于互操作性挑战,我不愿意只使用步骤定义和用 CommonJS 编写的支持代码。

我试图让它发挥作用的其他事情包括......

  • 根据 Node.js 约定,为原生模块文件提供.mjs扩展名(这导致它们被 Cucumber 的自动加载逻辑忽略,并使用--require显式加载步骤定义模块抛出Error [ERR_REQUIRE_ESM]: Must use import to load ES Module
  • 将原生模块包装在使用动态import()函数的 CJS 模块中; 问题是这种方法是异步的,即使导入承诺是由 CJS 模块导出的,Cucumber 似乎也不会在继续之前等待该承诺解决,因为该步骤被标记为未定义(表明步骤定义在本机模块中尚未注册)。

如果在我的能力范围内,我很乐意提交 PR 来帮助解决此问题,但我不熟悉 Cucumber.js 的内部工作原理,如果有人能指出我正确的方向,我将不胜感激。

accepted enhancement

最有用的评论

我使用.js扩展名和"type":"module"因为我预计这将成为长期的常态。

所有13条评论

我想让cucumber-js 与ESM 一起工作。 我们已经有一些关于它的其他问题。

我们可以创建一个 CLI 选项(或使用其他方式来寻找何时使用它),让 Cucumber-js 使用import而不是 require 并等待返回的 promise。

鉴于您添加的示例,我们可以专门为此添加一个测试用例并努力使其通过。 尝试切换需要导入并等待承诺,然后我遇到了另一个错误:

⋊> ~/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 导入而不是要求的 CLI 选项很适合我。 如果 Cucumber 寻找带有 .cjs 和 .mjs 扩展名的支持文件,并自动将它们分别视为 CommonJS 和本机模块,那也很棒。

编辑:只是想重申一下,如果可以的话,我很乐意为您提供帮助; 只是指出我正确的方向。

我可能只是导入模块默认值,然后从中访问 Given/When/Then 方法:
从“黄瓜”进口黄瓜;
黄瓜。给定(...);

这不起作用。 我收到同样的信息。
我在 package.json 中有 "type": "module" 并且我的项目的其余部分正在使用模块,所以改变它不是一个选项。

如果我使用其中任何一个:

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(),或从 project/package.json 中删除“type”:“module”。

如果我将step_definitions.js重命名step_definitions.cjsstep_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”并将cucumber安装到spec/node_modules

到目前为止,它似乎正在起作用。 一旦我创建了一些实际测试,让我们看看它是否仍然有效。

致维护者:这是我第一次使用黄瓜,到目前为止我很喜欢。 然而,这确实阻碍了我的进步。 我没有用下午的时间学习黄瓜,而是用它来调试导入。 我知道试图创建一个同时支持 ES6 导入和 CJS 的库是多么烦人,所以你有我的同情。

一个可能的解决方案:
如果生成第二个 dist 文件,则可以同时发布 CJS 和 ES6 构建:

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扩展名
  • 还有什么?

谢谢!

我倾向于遵循对所有 JavaScript 文件使用 .mjs 和 .cjs 扩展名的约定以避免歧义,在这种情况下我也倾向于这样做。 但是,如果在项目级别设置“type”:“module”并将它们保留为 .js 文件,如果这对更多用户来说是更好的解决方案,那将不会有太大的不便。

我使用.js扩展名和"type":"module"因为我预计这将成为长期的常态。

感谢您的反馈@adamlacoste @looeee

@davidjgoss #1589 合并是否足以关闭该问题? 还是我们需要别的东西?

感谢您对@aurelien-reeves 的鼓励!

@cucumber/cucumber 7.2.0 版现在在 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 仍然可以进行试验。

我一直在尝试与--esm标志一起给v7.2.0一个机会。 我在尝试转换为 ESM 的包的测试中使用 testdouble。

为了使用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一起使用的加载程序?

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

hdorgeval picture hdorgeval  ·  3评论

Niceplace picture Niceplace  ·  4评论

igniteram picture igniteram  ·  7评论

zanona picture zanona  ·  4评论

edwinwright picture edwinwright  ·  3评论