cypress-cucumber-preprocessor
cypress-cucumber-preprocessor copied to clipboard
Upgrade guide from `TheBrainFamily/cypress-cucumber-preprocessor`
Transfer of ownership
Due to personal reasons, the previous maintainers of this package are stepping down and handing the reigns over to me, a long-time contributor to the project and a user of it myself. This is a responsibility I'm very excited about. Furthermore, I'd like to thank @lgandecki ++ for all the work that they've done so far.
What's new :tada:
This implementation has been re-written from scratch in TypeScript, has more thorough test coverage and is filled with a bunch of new feature.
-
Support for the
Rulekeyword. -
The
cypress-tagshas been removed and made redundant. Specs containing no matching scenarios are automatically filtered, provided thatfilterSpecsis set totrue. -
Screenshots are automatically added to JSON reports (including that of failed tests).
-
Other attachments can be added to the report.
-
JSON reports are generated as a single file and manual, post-merging is no longer required.
-
The package is now located under the name
@badeball/cypress-cucumber-preprocessorand a deprecation notice will be posted in the old name once NPM transfer is complete. -
Methods such as
Given,WhenandThenare imported from@badeball/cypress-cucumber-preprocessor, IE. without the../stepspostfix. -
A large number of issues has implicitly been fixed due to architectural changes.
-
And(..)andBut(..)have been deprecated, read more here.
What's missing
- Bundled features files. This has been deprioritized, due to the associated maintenance cost, awkward implementation and my personal lack of convincing that it's necessary. Using esbuild gives near-instant compilation time of features and removes nearly all performance gain from bundled features anyway.
Changes to configuration
Nearly all configuration options has been removed and there's no distinction between "global" and "non-global" steps anymore. Steps are searched for using patterns and you can chose to include global steps or not.
Below are some configuration examples. Configuration is still implemented using cosmiconfig, meaning that your configuration can reside in multiple locations, such as package.json or .cypress-cucumber-preprocessorrc.json.
Global step definitions exaple
{
"name": "my project",
"depdendencies": {},
"devDepdendencies": {},
"cypress-cucumber-preprocessor": {
"stepDefinitions": "cypress/support/step_definitions/**/*.{js,ts}"
}
}
Step definitions besides scenario
{
"name": "my project",
"depdendencies": {},
"devDepdendencies": {},
"cypress-cucumber-preprocessor": {
"stepDefinitions": "cypress/e2e/[filepath].{js,ts}"
}
}
Step definitions in a directory besides the scenario
{
"name": "my project",
"depdendencies": {},
"devDepdendencies": {},
"cypress-cucumber-preprocessor": {
"stepDefinitions": "cypress/e2e/[filepath]/**/*.{js,ts}"
}
}
Combination of all of the above (default)
{
"name": "my project",
"depdendencies": {},
"devDepdendencies": {},
"cypress-cucumber-preprocessor": {
"stepDefinitions": [
"cypress/e2e/[filepath]/**/*.{js,ts}",
"cypress/e2e/[filepath].{js,ts}",
"cypress/support/step_definitions/**/*.{js,ts}"
]
}
}
Still have an issue?
Because this is a re-implementation and thorough test coverage was originally lacking, old issues may resurface and new issue arise. If you have an issue or just a question, please post it below and I'll try to help.
First off, thank you for your hard work maintaining this project and coordinating the transfer 👏🏻 I'm very excited to look over the changes and give them a test.
Are you planning to make GitHub releases for the versions you've created and published before the project was fully transferred? That seems like a logical place to start learning about changes.
Are you planning to make GitHub releases for the versions you've created and published before the project was fully transferred? That seems like a logical place to start learning about changes.
This is difficult because some of the version tags would conflict with one another. You can however see the history of the fork here (said link can also be found in the current changelog).
Hey @badeball I would like to second a thank you on your continued maintenance of this project, coordination of the transfer and the updates / refactoring you've done. I'm also excited to take a look and try them out and provide any useful feedback. Well done mate
@badeball - This is great work thanks for keeping it going. I have a question about the config upgrade. Previously steps in folders matching the feature file name would only work in the relevant feature. Is this covered by the config option above:
{
"name": "my project",
"depdendencies": {},
"devDepdendencies": {},
"cypress-cucumber-preprocessor": {
"stepDefinitions": "cypress/integration/[filepath]/**/*.{js,ts}"
}
}
I'm not sure I understand the difference between this one and 'Step definitions besides scenario'. Do we need to explicitly add the filepath for the scenario name or does this value get interpreted at runtime?
Hi, @andyg101
Notice the difference between the following two patterns
cypress/integration/[filepath]/**/*.{js,ts}
.. and
cypress/integration/[filepath].{js,ts}
These will match different files, pick whatever suits you the best.
I've manged to upgrade from 4.3.1 to 9.0.0 but having an issue with the ESBuild plugin. I get this error when i try to use it:
Transforming const to the configured target environment ("es5") is not supported yet
I'm not super familiar with esbuild. Have I missed some obvious configuration here? The only changes I made was to add this to my index.js
on(
'file:preprocessor',
createBundler({
plugins: [createEsbuildPlugin(config)],
}),
);
nb. I have got this working with browserify but I believe there is a performance benefit to using ESbuild
Hi, @andyg101
Notice the difference between the following two patterns
cypress/integration/[filepath]/**/*.{js,ts}.. and
cypress/integration/[filepath].{js,ts}These will match different files, pick whatever suits you the best.
Thanks @badeball. What I mean is if I have a feature like this
my-feature-with-unique-steps.feature
and a folder
cypress/integration/my-feature-with-unique-steps
Is that covered by the option(s) containing [filepath]?
update: I think i might have managed what I'm trying to do like this:
"cypress/integration/**/[filepath]/**/*.{js,ts}"
The pattern
cypress/integration/[filepath].{js,ts}
.. will for
cypress/integration/my-feature-with-unique-steps.feature
.. be expanded to
cypress/integration/my-feature-with-unique-steps.{js,ts}
.. which will match both cypress/integration/my-feature-with-unique-steps.js and cypress/integration/my-feature-with-unique-steps.ts.
I recommend reading more about patterns here: https://github.com/isaacs/node-glob
I'm not super familiar with esbuild. Have I missed some obvious configuration here? The only changes I made was to add this to my index.js
Do you by any chance have a tsconfig.json lying there as well? If so, what does it contain?
I'm not super familiar with esbuild. Have I missed some obvious configuration here? The only changes I made was to add this to my index.js
Do you by any chance have a tsconfig.json lying there as well? If so, what does it contain?
Yep here it is:
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"typeRoots": ["types", "../node_modules/@types"],
"types": [
"cypress",
"node",
"cypress-keycloak-commands"
],
},
"include": [
"./**/*.ts",
"./**/*.js"
]
}
Esbuild will look for that and configure its target with es5, in which const is apparently not supported. So you have to bump that to something that supports const. I'm guessing you can safely put es6 there as a minimum.
Thanks. I'll give that a go. Help much appreciated @badeball
Hmm. Now I get
node_modules/jju/lib/utils.js:1:18: ERROR: Could not resolve "fs"
That is likely because one of your step definitions is requiring / importing something, that eventually tries to require the fs module, which is unavailable in the browser. Browserify handles it somewhat differently than esbuild, a bit lenient. It would help to know what exactly it is you¨re doing. Can you provide me a repository which I can clone myself? Then I can easily point out the error.
I see jju there. That's a node module and won't work in the browser, browserify or not. Maybe browserify was able to shim out fs and you coincidentally avoided using anything in jju that used fs, but that was just lucky I guess.
I have successfully migrated to badeball/cypress-cucumber-preprocessor and am already observing much faster load times for feature files using webpack, kudos!
I am interested in even better performance from the esbuild example, but am getting similar issues with resolving node modules. The use case is that we have imported a package in a cypress plugin that uses node modules, which runs in cy.task outside of the browser. For any built-in node modules that this package requires, I get an error like this:
[ERROR] Could not resolve "stream" The package "stream" wasn't found on the file system but is built into node. Are you trying to bundle for node? You can use "platform: 'node'" to do that, which will remove this error.
Note that in the original webpack configuration the workaround is to use node-polyfill-webpack-plugin.
Do you have recommendations for how to work around this using esbuild?
Do you have recommendations for how to work around this using esbuild?
Can you try @esbuild-plugins/node-modules-polyfill as described here?
I tried that plugin and then got an error about buffer not being defined, so I added the node-globals-polyfill and set buffer to true, then I got require not defined so I set it to true, then got stuck when I got __dirname is not defined. I also tried setting platform: node in the configuration for createBundler but got similar errors.
So no answer yet but we can continue using webpack for now. I can try to use esbuild on a separate/new project to get a working proof of concept and revisit. Thanks for your help!
I see
jjuthere. That's a node module and won't work in the browser, browserify or not. Maybe browserify was able to shim outfsand you coincidentally avoided using anything injjuthat usedfs, but that was just lucky I guess.
jju is a package we use for parsing json5 (which we are using in our feature files in docstrings). Unfortunately I can't give you a copy of the repo I'm working on. I'm going to try the suggestion in https://esbuild.github.io/getting-started/#bundling-for-node to see if I can get that working.
Update: I tried using the following
on(
'file:preprocessor',
createBundler({
plugins: [createEsbuildPlugin(config)],
external: [config.projectRoot + 'node_modules/*'],
platform: 'node',
}),
);
This results in a TypeError
TypeError
The following error originated from your test code, not from Cypress.
> fn is not a function
When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.
Cypress could not associate this error to any specific test.
We dynamically generated a new test to display this failure.
Which I think is down to the following code (line 5705 is failing)

Have to admit I'm a bit out of my depth here in terms of next steps. I think that chai is bundled with Cypress itself so not sure how to fix that.
I was able to get it to compile with jju using a configuration shown below
import * as createBundler from "@bahmutov/cypress-esbuild-preprocessor";
import { createEsbuildPlugin } from "@badeball/cypress-cucumber-preprocessor/esbuild";
import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
export default (
on: Cypress.PluginEvents,
config: Cypress.PluginConfigOptions
): void => {
on(
"file:preprocessor",
createBundler({
plugins: [NodeModulesPolyfills(), createEsbuildPlugin(config)],
})
);
};
That worked @badeball. Thanks so much for the help!
Also I got the glob matching working as per your suggestion too.
@lgandecki, do you mind transferring the NPM ownership as well? It's currently a bit difficult to realize that there's a new version published (EG. https://github.com/badeball/cypress-cucumber-preprocessor/issues/657#issuecomment-1099124419).
@badeball this is awesome! I was able to get esbuild working with the following configuration similar to what you posted above:
on('file:preprocessor', createBundler({
plugins: [
NodeModulesPolyfills(),
GlobalsPolyfills({
process: true,
buffer: true,
}),
createEsbuildPlugin(config)
],
}));
Incredible performance gains switching to esbuild!
@jwmickey @badeball Thanks for that!!. The configuration you provided worked for the esbuild.
@badeball I do have one issue with the stepDefinitions path. Before I had the following config
"cypress-cucumber-preprocessor": {
"nonGlobalStepDefinitions": true,
},
This allowed me to use a common step folder in cypress/integration/common/[file].ts
While a feature would be in cypress/integration/[feature]/[filepath].feature and the related step definitions in cypress/integration/[feature]/[filepath]/**/*.ts
With the latest version I removed the nonGlobalStepDefinitions as it appears this is no longer necessary however when running cypress it is unable to pickup the step implementation in the cypress/integration/common/[file].ts
I should also mention the related file in here is my Background so my feature file looks like
Feature: Feature #1
Background:
Given X
And Y
Scenario: Scenario #1
Given 1
When 2
Then 3
The Given step in my Background is not being found which is in a common folder cypress/integration/common/[file].ts. Is there something I'm missing? I've also tried a combination of various stepDefinitions in my config to get this working but still no luck.
@hect1c I have the same set up as you I think and I found this works for common steps in cypress/integration/common
"cypress-cucumber-preprocessor": {
"stepDefinitions": "cypress/integration/common/**/*.{js,ts}"
}
@andyg101 Thanks I had tried that before and it didn't work but I just found out why.
@badeball It appears the cosmiconfig may not be working properly as my configurations were not working when using any variation outside of the package.json . When I moved them back to package.json I got it all working with the following
"cypress-cucumber-preprocessor": {
"stepDefinitions": [
"cypress/integration/common/**/*.{js,ts}",
"cypress/integration/[filepath]/**/*.{js,ts}",
"cypress/integration/[filepath].{js,ts}"
]
}
This is how the configuration should look like if placed somewhere aside from package.json:
{
"stepDefinitions": [
"cypress/integration/common/**/*.{js,ts}",
"cypress/integration/[filepath]/**/*.{js,ts}",
"cypress/integration/[filepath].{js,ts}"
]
}
.. notice it is not wrapped in { "cypress-cucumber-preprocessor": { } }.
@badeball Hi Jonas,
I was looking at setting up the json output yesterday. Are the additional parameters you mention meant to go in the json config as well e.g.
"json": {
"enabled": true,
"formatter": "/path/to/formatter"
"output":"/path/to/output.json"
}
}
Also, what if I want to have this work on both windows and linux, is there any way to specify an alternative path to the formatter?
Are the additional parameters you mention meant to go in the json config as well e.g.
I don't know which additional parameters you're referring to here, but I'm guessing yes.
Also, what if I want to have this work on both windows and linux, is there any way to specify an alternative path to the formatter?
No, that's not possible. The ability to specify custom location isn't really meant to be done by default. Every developer might have different desire as to where to put these kind of things. Hence, the preprocessor will search for the executable in your PATH if no location is specified. That's what both your Linux and Windows users should do, put the executable somewhere that makes it available from PATH.
@badeball I have a question about the filterspec so that it wont report test from other feature files. The example below is created in Typescript but for our project we arent using typescript so do you have an example how to resolve the async await in old JS notation. Since we are using the normal module.exports function style.
import { addCucumberPreprocessorPlugin } from "@badeball/cypress-cucumber-preprocessor";
export default async (
on: Cypress.PluginEvents,
config: Cypress.PluginConfigOptions
): Promise<Cypress.PluginConfigOptions> => {
await addCucumberPreprocessorPlugin(on, config);
// Make sure to return the config object as it might have been modified by the plugin.
return config;
}
@Xvier CJS:
const {
addCucumberPreprocessorPlugin
} = require("@badeball/cypress-cucumber-preprocessor");
module.exports = async (on, config) => {
await addCucumberPreprocessorPlugin(on, config);
// Make sure to return the config object as it might have been modified by the plugin.
return config;
};