Error: Import statement cannot be used outside a module (@cucumber/cucumber) while using Node.JS, Playwright, and Cucumber framework

I encountered an issue while attempting to compile my Node.js code that is compliant with ECMAScript 6:

$ npx cucumber-js --require features/step_definitions/steps.ts --exit
 
import { Before, Given, When, Then } from "@cucumber/cucumber";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at wrapSafe (internal/modules/cjs/loader.js:1029:16)
    at Module._compile (internal/modules/cjs/loader.js:1078:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1143:10)
    at Module.load (internal/modules/cjs/loader.js:979:32)
    at Function.Module._load (internal/modules/cjs/loader.js:819:12)
    at Module.require (internal/modules/cjs/loader.js:1003:19)
    at require (internal/modules/cjs/helpers.js:107:18)
    at /Users/sam.levene/QA/playwright-automation/node_modules/@cucumber/cucumber/lib/api/support.js:18:32
    at Array.map (<anonymous>)
    at getSupportCodeLibrary (/Users/sam.levene/QA/playwright-automation/node_modules/@cucumber/cucumber/lib/api/support.js:18:18)

Below is the code for analysis:

steps.ts

import { Before, Given, When, Then } from "@cucumber/cucumber";
import { Page, chromium } from "@playwright/test";
import { HomePage }  from "../../pages/HomePage";
import { SignInPage } from "../../pages/SignInPage";
import { SignInParameters } from "../../support/SignInParameters";

let homePage: HomePage;
let signInPage: SignInPage;
let signInParameters: SignInParameters;
let page: Page;

Before(async function() {
    var browser = await chromium.launch({
        headless: false,
    });
    var context = await browser.newContext();
    var page = await context.newPage();
    homePage = new HomePage(page);
    signInPage = new SignInPage(page);
    signInParameters = new SignInParameters();
});

(Step definition file. you get the gist.)

The error seems to indicate that cucumber-js does not support TypeScript type imports. However, I have specified the latest version of Cucumber-js in my Npm modules:

{
  "name": "playwright-poc",
  "version": "0.0.1",
  "description": "A Proof of Concept for Playwright",
  "scripts": {
    "test": "npx cucumber-js --require features/step_definitions/steps.ts --exit"
  },
  "type": "module",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@playwright/test": "^1.31.2",
    "@cucumber/cucumber": "^9.0.1"
  }
}

UPDATE: I tried changing the file extension to .mjs as suggested by @ParzhFromUkraine and received the following error:

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/sam.levene/QA/playwright-automation/features/step_definitions/steps.mjs
    at new NodeError (internal/errors.js:322:7)
    at Module.load (internal/modules/cjs/loader.js:977:11)
    at Function.Module._load (internal/modules/cjs/loader.js:819:12)
    at Module.require (internal/modules/cjs/loader.js:1003:19)
    at require (internal/modules/cjs/helpers.js:107:18)
    at /Users/sam.levene/QA/playwright-automation/node_modules/@cucumber/cucumber/lib/api/support.js:18:32
    at Array.map (<anonymous>)
    at getSupportCodeLibrary (/Users/sam.levene/QA/playwright-automation/node_modules/@cucumber/cucumber/lib/api/support.js:18:18)
    at runCucumber (/Users/sam.levene/QA/playwright-automation/node_modules/@cucumber/cucumber/lib/api/run_cucumber.js:34:53)
    at async Cli.run (/Users/sam.leven/QA/playwright-automation/node_modules/@cucumber/cucumber/lib/cli/index.js:50:29)

Are there any suggestions on how to resolve this issue?

Answer №1

After a tremendous collaborative effort from the cucumber-js developers, the missing pieces finally fell into place for me. I have outlined the solution here for future reference in case others encounter the same challenges.

To begin with, I realized that I was lacking cucumber.js and tsconfig.json files containing the essential configurations:

cucumber.js

export default [
    "--import features/**/*.ts",
    "--publish-quiet"
].join(" ");
tsconfig.json

{
    "compilerOptions": {
      "allowJs": true,
      "baseUrl": ".",
      "forceConsistentCasingInFileNames": true,
      "module": "esnext",
      "moduleResolution": "nodenext",
      "noImplicitAny": true,
      "noImplicitReturns": true,
      "noImplicitThis": true,
      "noUnusedLocals": true,
      "sourceMap": true,
      "strictNullChecks": true,
      "esModuleInterop": true,
      "allowSyntheticDefaultImports": true,
      "target": "es6",
      "strict": true,
      "resolveJsonModule": true,
      "isolatedModules": true,
      "noEmit": true
    },
    "exclude": [
      "node_modules",
      ".idea",
      ".vscode"
    ],
    "include": [
      "features/**/*.ts",
    ]
  }

Given that this setup involves ES-Next, an experimental feature, I also had to modify the package.json file to include references to the ESM loader and install ts-node:

package.json

{
  "name": "playwright-poc",
  "version": "0.0.2",
  "description": "A Proof of Concept for Playwright",
  "scripts": {
    "test": "NODE_OPTIONS=\"--loader ts-node/esm\" npx cucumber-js --parallel 10 --exit"
  },
  "type": "module",
  "keywords": [],
  "author": "Sam Levene",
  "license": "MIT",
  "devDependencies": {
    "@cucumber/cucumber": "^9.0.1",
    "@playwright/test": "^1.31.2",
    "ts-node": "^10.9.1"
  }
}

Eventually, adjustments were required in my step definitions file to correctly reference and recognize the imports:

steps.ts

import { Before, Given, When, Then } from "@cucumber/cucumber";
import { Page, chromium } from "@playwright/test";
import { HomePage }  from "../../pages/HomePage.js";
import { SignInPage } from "../../pages/SignInPage.js";
import { ForgotPasswordPage } from "../../pages/ForgotPasswordPage.js";
import { SignInParameters } from "../../support/SignInParameters.js";

let homePage: HomePage;
let signInPage: SignInPage;
let signInParameters: SignInParameters;
let forgotPasswordPage: ForgotPasswordPage;
let page: Page;

Before(async function() {
    var browser = await chromium.launch({
        headless: false,
    });
    var context = await browser.newContext();
    page = await context.newPage();
    homePage = new HomePage(page);
    signInPage = new SignInPage(page);
    forgotPasswordPage = new ForgotPasswordPage(page);
    signInParameters = new SignInParameters();
});

Please note that despite the .js extensions mentioned in steps.ts, the referenced files are actually .ts files

Answer №2

There are two ways to tackle this issue -

  • You can opt for using require instead of directly importing the modules

For example, you could do:

const { Given, When, Then } = require('@cucumber/cucumber');
  • Alternatively, you can include the following in your package.json file to specify that the type is a module.

Add this line in your package.json:

{
   "type": "module"
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for properly implementing a bcrypt comparison within a promise?

Previously, my code was functioning correctly. However, it now seems to be broken for some unknown reason. I am using MariaDB as my database and attempting to compare passwords. Unfortunately, I keep encountering an error that says "Unexpected Identifier o ...

Different applications of data streaming apart from multimedia content

Exploring the various applications of streaming, particularly when sending data from a server to a visual client such as a web browser or an application, has sparked my curiosity. While I grasp the fundamental idea of transmitting data in chunks rather t ...

Utilizing properties from the same object based on certain conditions

Here's a perplexing query that's been on my mind lately. I have this object with all the styles I need to apply to an element in my React app. const LinkStyle = { textDecoration : 'none', color : 'rgba(58, 62, 65, 1)', ...

Node is throwing a 302 error on Localhost:3000

Looking for some guidance as a beginner trying to create and run a nodejs application. Encountering an error while running server.js via nodemon, the console displays the following: Express server listening on port 3000 Mongoose default connection open t ...

Error in Nestjs Swagger: UnhandledPromiseRejectionWarning - The property `prototype` cannot be destructed from an 'undefined' or 'null' object

Currently, I am in the process of developing a Nestjs REST API project and need to integrate swagger. For reference, I followed this repository: https://github.com/nestjs/nest/tree/master/sample/11-swagger However, during the setup, I encountered the foll ...

Delving into the World of ES6 Symbols

Throughout my experience with various programming languages, from C# to Lisp to Scala to Haskell, symbols have consistently behaved as singleton objects. This means that any two symbols with the same name are guaranteed to be identical. In Racket: (equal? ...

Building a Next.js application that supports both Javascript and Typescript

I currently have a Next.js app that is written in Javascript, but I am looking to transition to writing new code in Typescript. To add Typescript to my project, I tried creating a tsconfig.json file at the project root and then ran npm install --save-dev ...

In Angular components, data cannot be updated without refreshing the page when using setInterval()

Here's the Angular component I'm working with: export class UserListComponent implements OnInit, OnDestroy { private _subscriptions: Subscription; private _users: User[] = []; private _clickableUser: boolean = true; constructor( priv ...

Implementing Node.JS ajax to update current JSON information

I am seeking assistance in updating data within a JSON file using NODE.JS. Currently, my method adds the data with the same ID as expected. However, upon receiving the data back, it eliminates the last duplicate because it encounters the old value first. I ...

Can you please explain the meaning of this statement in JavaScript/Node.js with regards to the importance of the => operator and the async and await keywords being used here?

(1) This snippet of code is used to hash a password, but the syntax may be unclear. Why does it utilize async and await in this manner? And why doesn't the => symbol seem to define a function? const hashPassword = async password => await bcrypt.ha ...

Dismiss the Popover in Ionic 2

After opening a popover that redirects me to another page and then returning to the root page (popToRoot), I reload the data/dom upon an event and dismiss the popup once the json data is received from the server. Everything works smoothly with a lengthy ti ...

Is it possible to incorporate a combination of es5 and es2015 features in an AngularJS application?

In my workplace, we have a large AngularJS application written in ES5 that is scheduled for migration soon. Rather than jumping straight to a new JS framework like Angular 2+ or React, I am considering taking the first step by migrating the current app to ...

Obtain details regarding a worker's collision

This code snippet is being used to manage cluster crashes within a node application cluster.on('exit', function (worker, code, signal) { console.log("error in cluster",worker); console.log("cluster code",code); console.l ...

Encountered a problem when trying to import the function "createToken" into a Node.js middleware

I have developed a model called users in which I included a method named generateToken for generating web tokens. This model is being used with the Sequelize ORM. module.exports = (sequelize, Sequelize) => { const Tutorial = sequelize.define("u ...

How do I redirect with a GET method after calling the *delete* method in Node / Express server?

As someone new to AJAX and Node, I encountered a dilemma that I hope to get some guidance on. Here's the issue: I have a DELETE ajax call that removes a row from the database and I want to redirect back to the same route with a GET method afterwards. ...

The instance does not have a defined "key" property or method in this Vue/Laravel application

When I attempt to delete a register in my application, I encounter a Vue-warn message: The property or method "key" is referenced during render but is not defined on the instance. Make sure that this property is reactive by initializing it in the data o ...

Modifying iframe src using click event from a separate component in Angular 10

I am looking to dynamically update the src attribute of an iframe when the menu bar is clicked. The menu bar resides in a separate component and includes a dropdown menu for changing languages. Depending on which language is selected, I want to update the ...

`req.user` seems to be unresolved, but it is actually defined

Currently, I am working on developing an Express.js application that utilizes Passport.js for authentication in an administration panel. The program is functioning correctly at the moment, with my app.js initializing passport and setting up sessions proper ...

Is it possible to configure the async.retry method to retry even upon successful queries, depending on a specific condition?

Currently, I am delving into the node.js async module and wondering if it's possible to modify the behavior of the async.retry method. Specifically, I'd like it to retry even on successful operations but halt based on a certain condition or respo ...

Encountered a TypeScript error: Attempted to access property 'REPOSITORY' of an undefined variable

As I delve into TypeScript, a realm unfamiliar yet not entirely foreign due to my background in OO Design, confusion descends upon me like a veil. Within the confines of file application.ts, a code structure unfolds: class APPLICATION { constructor( ...