Skip to content

Native JS module support in step definitions and support code #1304

Closed
@adamlacoste

Description

@adamlacoste

Problem

When I attempt to run Cucumber.js with a step definition defined in a native ECMAScript module (as documented here), the attempt fails with a warning that I am using CommonJS's require() on an ES module.

Cucumber.js version: 6.0.5
Node version: 13.8.0

Steps to reproduce

  1. Setup a basic NPM package directory using npm init and npm i cucumber

    • Set "type": "module" in the package.json file to ensure that JS files will be treated as native modules
  2. Create a basic feature file, features/mjs.feature:

     Feature: Native JS Modules
    
         Scenario: Load a native JS module step definition
             Given I have 42 cucumbers in my belly
    
  3. Create a basic step definition file, features/step_definitions.js:

     import { Given } from "cucumber";
     
     Given("I have {int} cucumbers in my belly", function (cucumberCount) {
         console.log("Step parsed.");
     });
    
  4. Attempt to run Cucumber:

     $ ./node_modules/.bin/cucumber-js
    

Expected result

The step definition module should be loaded and used to parse the step.

Actual result

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)

Closing

For what it's worth, I really appreciate the work that the Cucumber.js team has put in on this project, it has been a major asset to me and my company. My team has invested some time in building some shared application components in native JS modules, and until recently I had been assuming (based on the import syntax in the most current documentation) that this stuff would work with Cucumber when we got around to integrating with our test framework. That appears not to be the case, though. Since the shared component is already written and integrated into other parts of our application, I'm reluctant use only step definitions and support code written in CommonJS because of interoperability challenges.

Other things I attempted to get this to work included...

  • Giving the native module files .mjs extensions, per Node.js convention (this caused them to be overlooked by Cucumber's auto-loading logic, and using --require to explicitly load the step definition module threw Error [ERR_REQUIRE_ESM]: Must use import to load ES Module
  • Wrapping the native modules in CJS modules which use the dynamic import() function; the problem was that this approach is asynchronous, and even if the import promise is exported by the CJS module, Cucumber doesn't appear to wait for that promise to resolve before proceeding, because the step was marked as undefined (suggesting that the step definition in the native module was not yet registered).

If it's within my ability, I'd be happy to submit a PR to help resolve this issue, but I'm unfamiliar with the internal workings of Cucumber.js and would appreciate if someone could point me in the right direction.

Metadata

Metadata

Assignees

Labels

✅ acceptedThe core team has agreed that it is a good idea to fix this⚡ enhancementRequest for new functionality

Type

No type

Projects

Status

Released

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions