Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using a tsconfig.json file with cucumber-js #1139

Closed
desm opened this issue Sep 25, 2018 · 26 comments
Closed

using a tsconfig.json file with cucumber-js #1139

desm opened this issue Sep 25, 2018 · 26 comments

Comments

@desm
Copy link

desm commented Sep 25, 2018

When using cucumber-js with TypeScript, I can't figure out how to get a tsconfig.json to take effect.

cucumber version: 5.0.0
node version: v8.11.3
os: OS X El Capitan 10.11.6

At my project's root, I've got a package.json that contains:

{
  "name": "...",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test-features": "cucumber-js test/features --require-module ts-node/register --require 'test/features/**/*.ts'",
    ...

Also at project root, a tsconfig.json file that contains something like:

{
  "compilerOptions": {
    "baseUrl": ".", // This must be specified if "paths" is.
    "paths": {
      "*": [
        "src/commands/*"
      ] // This mapping is relative to "baseUrl"
    }
  }
}

To run cucumber, I type npm run test-features

@arjunjh10
Copy link

In one of the Cucumber Projects in my org, what I did was have a cucumber-runner.ts custom test runner as I was launching cucumber in parallel using nodeJs child processes.

so in my package.json:

 "scripts": {
    "cucumber": "ts-node --project <ts-config.json file location> cucumber-runner.ts",

hope it helps

@aseeeem
Copy link

aseeeem commented Oct 7, 2018

Does your tsconfig.json have a custom name of some sort? According to the ts-node README it should automatically be loading the tsconfig.json file

@sormy
Copy link

sormy commented Nov 24, 2018

Use latest @babel + @babel/preset-typescript+ @babel/register.
By the way @babel/register won't be able to pick babel.config.js or .babelrc or .babelrc.js
The workaround is to create an extra file like _init_.ts in source tree and make sure that it will be alphabetically first so will be loaded first when glob pattern will be expanded. See below for my working example.

I guess it is worth of it to add that into README ;-)

_init_.ts example:

// babel-register can't load configuration from babel.config.js
// load babel configuration explicitly here
// filename should be in top of folder to be loaded first

// @ts-ignore
// tslint:disable-next-line
require("@babel/register")({
  extensions: [".js", ".ts"],
  presets: [
    ["@babel/preset-env", { targets: "node 10" }],
    ["@babel/preset-typescript", { allExtensions: true }],
  ],
  plugins: [
    "@babel/proposal-class-properties",
    "@babel/proposal-object-rest-spread",
  ],
})

cucumber run:

cucumber-js --require-module @babel/register --require 'src/**/*.ts' --format json:build/cucumber.json features

folder structure:

features/simple_math.feature
src/support/initBabel.ts
src/support/initWorld.ts
src/support/steps.ts
src/support/World.ts
tsconfig.json

tsconfig.json:

{
  "compilerOptions": {
    // Target latest version of ECMAScript.
    "target": "esnext",
    // Search under node_modules for non-relative imports.
    "moduleResolution": "node",
    // Process & infer types from .js files.
    "allowJs": true,
    // Don't emit; allow Babel to transform files.
    "noEmit": true,
    // Enable strictest settings like strictNullChecks & noImplicitAny.
    "strict": true,
    // Disallow features that require cross-file information for emit.
    "isolatedModules": true,
    // Import non-ES modules as default imports.
    "esModuleInterop": true,

    "allowSyntheticDefaultImports": true
  },
  "include": [
    "src/**/*"
  ]
}

@nikolaypisanchev
Copy link

As far as I remember, in this context ts-node would expect the tsconfig to be in the test folder. You could try creating a config in the test folder that extends the one in the root folder or setting the TS_NODE_PROJECT environment variable.

@x80486
Copy link

x80486 commented Aug 6, 2019

Any updates on this one?

I'm also trying to setup something similar, and while it works for the most part, I still have some issues when using tsconfig-paths...it keeps saying that TypeError: cucumber_1.Before is not a function.

Funny thing is, if I delete the cucumber.js file and put everything in the CLI it works, so I believe that somehow, Cucumber is not quite recognizing that completely.

@jackall3n
Copy link

@x80486 did you have any luck with TypeError: cucumber_1.Before is not a function? or did you just remove cucumber.js?

@x80486
Copy link

x80486 commented Mar 1, 2020

@jackall3n, I can't remember right now. I had to move to some other project. Let me see if I can go back to try this one again with newer versions of Cucumber.js...probably something changed 🙄

@Izhaki
Copy link
Contributor

Izhaki commented Apr 21, 2020

Closing this due to inactivity. Will be happy to re-open if more details provided.

@Izhaki Izhaki closed this as completed Apr 21, 2020
@x80486
Copy link

x80486 commented Sep 10, 2020

@Izhaki, the details are on the issue. For instance, if you want to reproduce this, grab this project and change the following in tsconfig.json (leaving the rest of key/values untouched):

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@model/*": ["./support/model/*"]
    },
}

...then execute npm run test — it will fail again with TypeError: cucumber_1.Before is not a function ....

@scotch83
Copy link

@arjunjh10

"cucumber": "ts-node --project <ts-config.json file location> cucumber-runner.ts",

I am interested to hear how such a file would look like

@DerZyklop
Copy link

@arjunjh10 I am also interested in the content of your cucumber-runner.ts.
@scotch83 if you managed to figure it out, please let me/us know.

@adamf321
Copy link

In case any else stumbles upon this trying to get baseUrl to work with ts-node / cucumber-js...

You need to create a setup file:

// cucumber.setup.js

require("ts-node").register({
  transpileOnly: true,
  require: ["tsconfig-paths/register"],
  compilerOptions: {
    "module": "commonjs",
    "resolveJsonModule": true,
    "baseUrl": ".",
  },
});

require: ["tsconfig-paths/register"] was the part which tripped me up.

Then reference it in your cucumber config file:

// cucumber.js

module.exports = {
  default: [
    "--require cucumber.setup.js",
    "--require tests/bdd/**/*.ts",
    "--publish-quiet",
  ].join(" "),
};

@mattwynne
Copy link
Member

mattwynne commented Feb 23, 2022

Note that there's now a TypeScript reference example, here.

@adamf321
Copy link

adamf321 commented Feb 23, 2022

Yes, I saw that. But it uses relative module imports so doesn't show how to solve this problem. It's more of a ts-node quirk than a Cucumber issue specifically.

@MarufSharifi
Copy link

I am using NX for my monorepo, it works well, but when i import something from my local libs it tell me cannot find module "My Local Lib Name" , the problem is not with NX because it works fine with other stuff but not with cucumber-js.

My local libs paths are defined in my tsConfig.

How can i solve this issue?

@Izhaki
Copy link
Contributor

Izhaki commented Mar 27, 2022

@MarufSharifi To be honest, this is not an issue with CucumberJS. This is about module resolution, monorepos, build configurations. StackOverflow may be a better place to ask these questions.

Anyhow, it is impossible to say without more details.

As a pointer, whatever transpile your code when running cucumber should be configured to account for aliases (aka paths in tsconfig). If your cucumber setup is using ts-node, this means typescript is transpiling your code and so long it is pointed to the correct tsconfig it should work. If you are using babel to transpile typescript code, there are various options - either configure the aliases in babel (import plugin), or configure babel to read the aliases from the tsconfig file, or to use something like tsconfig-paths.

You'll need to provide more details on how exactly the transpilation of your code is set up in cucumber.

@MarufSharifi
Copy link

Hi, Thanks for reply,

This is my babel transpiling config

apps/web/brands-e2e/init.ts
--require-module @babel/register

This is init.ts file code

require('@babel/register')({ extensions: ['.js', '.ts'], presets: [ ['@babel/preset-env', { targets: 'node 10' }], ['@babel/preset-typescript', { allExtensions: true }], ], plugins: ['@babel/proposal-class-properties', '@babel/proposal-object-rest-spread'], });

I can easly switch to ts-node if it helps.

what else do i have to add?

@MarufSharifi
Copy link

tsConfig works fine in compile time, it know that we have those local libs, but in runtime it doesn't work, it tell me I cannot find this module.

I mean it suggest me those local libs when importing them.

I asked from NX community as well, they told me, "You may need a flag to tell the CLI you are calling what tsconfigs to use"

@Izhaki
Copy link
Contributor

Izhaki commented Mar 28, 2022

There is a bit more degree of freedom with babel, you can try:

That should give you an idea.

Or just use ts-node as in the official example: https://github.com/cucumber-examples/cucumber-js-examples/tree/main/examples/typescript-node

@MarufSharifi
Copy link

I am using NX for my monorepo, it works well, but when i import something from my local libs it tell me cannot find module "My Local Lib Name" , the problem is not with NX because it works fine with other stuff but not with cucumber-js.

My local libs paths are defined in my tsConfig.

How can i solve this issue?

Finally I solved the problem, just added babel-plugin-tsconfig-paths to my babel configuration

https://www.npmjs.com/package/babel-plugin-tsconfig-paths

@m-thompson-code
Copy link

m-thompson-code commented Aug 25, 2022

In case someone wants a solution that doesn't require using babel and want to stick to using ts-node/register:

/**
 * Calls cucumber-js and overrides default tsconfig.json
 */
const { spawn } = require("child_process");

const cli = "npx cucumber-js --config src/app/cucumber.js";

const [command, ...args] = cli;

const env = {
    // all existing process environment variables
    ...process.env,
    // This is required since `ts-node/register`
    // defaults to using the root tsconfig.json of the project
    // and the problem is that Nx provides several tsconfig files
    //
    // Also Cucumber doesn't offer a way to set this for ts-node
    // You can override ts-node's behavior using this environment variable
    TS_NODE_PROJECT: "src/app/tsconfig.json",
};

spawn(command, args, { 
    env,
    // Sends output from child process to parent process
    stdio: "inherit"
});

For reference, here is my cucumber configuration:

module.exports = {
  default: {
    parallel: 1,
    requireModule: ['ts-node/register'],
    publishQuiet: true,
    format: [
        'html:dist/cucumber/app/cucumber-report.html'
     ],
    paths: ['src/app/src/features/**/*.feature'],
    require: ['src/app/src/step-definitions/**/*.ts'],
  },
};

@fddayan
Copy link

fddayan commented Sep 9, 2022

this made it work to me

TS_NODE_PROJECT=./features/tsconfig.json npx cucumber-js

@m-thompson-code
Copy link

this made it work to me

TS_NODE_PROJECT=./features/tsconfig.json npx cucumber-js

Is there a way to do this for Windows?

@akwasin
Copy link

akwasin commented Apr 22, 2023

Not sure about what the cucumber-runner.js contains but it could be a link to node_modules/@cucumber/cucumber/bin/cucumber.js

This is how i solved the issue of using having a project with module: ESNext and needing a separate tsconfig with CommonJS to run cucumber.

(npm run automation)
"automation": "ts-node --project src/tests/tsconfig.commonjs.json ./node_modules/@cucumber/cucumber/bin/cucumber.js src/tests/features/**/*.feature --require-module ts-node/register --require src/tests/test.setup.ts --require src/tests/step-definitions/**/*.spec.ts",

We have a react app with standard ./src folder and has a subfolder with features and step-definitions. This subfolder also holds tsconfig.commonjs.json that extends base tsconfig.json

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "module": "CommonJS"
  }
}

@delairec
Copy link

I had some problems while configuring cucumber to run with typescript and using dedicated tsconfig file, so I want to add some information here :

  1. it is possible to use a simple command in package.json, like cucumber-js features/**/*.feature
  2. in cucumber-js the (minimal) configuration is :
// cucumber.js
let common = [
  '--require cucumber.setup.js',          // Load setup file
  '--require features/**/*.step.ts',      // Load step definitions
].join(' ');

module.exports = {
  default: common
};

  1. then you are able to pass a tsconfig via cucumber.setup.js, like this example :
// cucumber.setup.js
const tsconfig = {
  compilerOptions: {
    'module': 'commonjs',
  }
};
require('ts-node').register(tsconfig);

Note : in my case, the error was coming from the line '--require-module ts-node/register' in cucumber.js. If you use the setup file to require ts-node, do not forget to remove the require-module from cucumber.js file or your code will be transpiled twice. The first transpilation will be on typescript code, the second on js result from first transpilation, so it will cause many weird errors.

@mattwynne
Copy link
Member

Thanks for sharing @delairec - feel free to send a PR updating the existing example or adding a new one: https://github.com/cucumber/cucumber-js-examples/tree/main/examples/typescript-node

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests