I have the following error when I try and compile my ECMAScript 6 compliant Node.js code:
$ 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)
Here's my 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.)
it seems as though the error is saying that cucumber-js doesn't support TypeScript type imports? But in my Npm modules, I am specifying the latest Cucumber-js version:
{
"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"
}
}
EDIT I attempted to follow the link from @ParzhFromUkraine and got the following error by editing the file to a .mjs:
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.levene/QA/playwright-automation/node_modules/@cucumber/cucumber/lib/cli/index.js:50:29)
Can anyone assist with this?
With much effort from the devs at cucumber-js; I finally figured out what I was missing and how to fix it, so I'm now providing this here for future reference if anyone else is having the same issues.
Firstly, I was missing cucumber.js
and tsconfig.json
files with the following information:
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",
]
}
As this is using ES-Next; which is experimental, I also needed to edit the package.json such that it referenced the ESM loader and also installed 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"
}
}
finally, I also had to edit my step definitions file to re-reference the imports such that it recognised the files correctly:
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();
});
NB: Even though in the steps.ts it says the imports are .js
files, they are still .ts
files