This document describes breaking changes and how to upgrade. For a complete list of changes including minor and patch releases, please refer to the changelog.
parseGherkinMessageStream
was a way to process a stream of envelopes from Gherkin and resolve to an array of filtered, ordered pickle Ids. The PickleFilter
class was used to provide a filter to the aforementioned function. These interfaces included internal implementation details from Cucumber which were difficult to assemble. To adapt, pivot to the loadSources
function from the JavaScript API, or raise an issue if you feel your use case isn't catered for.
The Runtime
class was used internally to represent an instance of the serial test case runner. Its interface included internal implementation details from Cucumber which were difficult to assemble. To adapt, pivot to the runCucumber
function from the JavaScript API, or raise an issue if you feel your use case isn't catered for.
Configuration files must now be one of our supported extensions (.json
, .yaml
, .yml
, .cjs
, .js
, .mjs
). JavaScript files are now loaded with the appropriate mechanism based on the file extension and package type. If you previously relied on our internal usage of require()
to dynamically transpile, you'll instead need to transpile beforehand and point Cucumber at the transpiled output.
Custom formatters and snippet syntaxes are now always loaded with await import()
. If you previously relied on our internal usage of require()
to dynamically transpile, you'll instead need to transpile beforehand and point Cucumber at the transpiled output.
If no support code is specified with either the import
or require
options, we'll now load files from the default paths with await import()
. If you need the use of require()
for your setup to work, you'll need to use that option explicitly.
Previously, string attachments were included as plain text in the JSON formatter output, where other attachments were Base64 encoded. This meant for consumers, it was ambiguous whether any attachment was Base64 encoded or not. Now, all attachments are Base64 encoded regardless of how they were initially attached.
Generator step definitions were removed in 8.0.0
; we've now removed the associated snippet interface too. So if you have some configuration like:
{
"formatOptions": {
"snippetInterface": "generator"
}
}
Then you'll need to change that value to one of synchronous
, async-await
, promise
or callback
.
Generator functions used in step definitions (function*
with the yield
keyword)
are not natively supported anymore with cucumber-js.
You may consider using async
/await
rather than generators.
You can still use generators as before but you need to add your own dependencies
to bluebird
and is-generator
. Cucumber-js will no display explicit error message
anymore in case you use a generator without wrapping it properly.
const isGenerator = require('is-generator')
const {coroutine} = require('bluebird')
const {setDefinitionFunctionWrapper} = require('@cucumber/cucumber')
setDefinitionFunctionWrapper(function (fn) {
if (isGenerator.fn(fn)) {
return coroutine(fn)
} else {
return fn
}
})
In the argument passed to your After
hook function, the result
no longer has a willBeRetried
property; this is now available at the top level of the object.
The Cli
class is sometimes used to run Cucumber programmatically. We've had to make a few breaking changes:
getConfiguration
,initializeFormatters
andgetSupportCodeLibrary
methods are removed- The constructor object has two new required properties:
stderr
- writable stream to which we direct warning/error output - you might just passprocess.stderr
env
- environment variables from which we detect some configuration options - you might just passprocess.env
In general for programmatic running (including those removed methods) we'd advise switching to the new API which is designed for this purpose.
Previously, you could require
anything directly from Cucumber's internals e.g. require('@cucumber/cucumber/lib/formatter/helpers')
. As part of adding ESM support we've added subpath exports, which restricts where Node.js can resolve modules from within the package. Deep requires are still possible but in a more limited way e.g. no implicit resolving of /index.js
with the above example. In a future release we'll remove the capability for deep requires entirely, so we'd advise addressing any instances in your code (here's an example). Everything you need should be available via the main entry point, but if something's missing please raise an issue.
When providing the path to a custom formatter or snippet syntax:
- For relative paths, you now need to ensure it begins with a
.
(this was already the case for custom formatters as of 7.0.0; snippet syntaxes are being changed to match) - For absolute paths, you now need to provide it as a valid
file://
URL
These CLI options have been removed:
--retryTagFilter
- the correct option is--retry-tag-filter
--predictable-ids
- this was only used for internal testing
Cucumber is now published at @cucumber/cucumber
instead of cucumber
. To upgrade, you'll need to remove the old package and add the new one:
$ npm rm cucumber
$ npm install --save-dev @cucumber/cucumber
You'll need to update any import
/require
statements in your support code to use the new package name.
(The executable is still cucumber-js
though.)
The result object passed as the argument to your After
hook function has a different structure.
Previously in cucumber
:
{
"sourceLocation": {
"uri": "features/example.feature",
"line": 7
},
"pickle": {...},
"result": {
"duration": 660000000,
"status": "failed",
"exception": {
"name": "AssertionError",
"message": "...",
"showDiff": false,
"stack": "..."
},
"retried": true
}
}
Now in @cucumber/cucumber
:
{
"gherkinDocument": {...}, // schema: https://github.com/cucumber/common/blob/messages/v16.0.1/messages/jsonschema/GherkinDocument.json
"pickle": {...}, // schema: https://github.com/cucumber/common/blob/messages/v16.0.1/messages/jsonschema/Pickle.json
"testCaseStartedId": "[uuid]",
"result": {
"status": "FAILED", // one of: UNKNOWN, PASSED, SKIPPED, PENDING, UNDEFINED, AMBIGUOUS, FAILED
"message": "...", // includes stack trace
"duration": {
"seconds": "0",
"nanos": 660000000
}
}
}
The underlying event/data model for cucumber-js is now cucumber-messages, a shared standard across all official Cucumber implementations. This replaces the old "event protocol".
If you maintain any custom formatters, you'll need to refactor them to work with the new model. The basics of a Formatter
class are the same, and the EventDataCollector
is still there to help you with tracking down data, but the names of events and shape of their data is different. It's worth checking out the implementations of the built-in formatters if you need a pointer.
We now support referring to custom formatters on the path by module/package name, for example:
$ cucumber-js --format @cucumber/pretty-formatter
This does mean that if you want to point to a local formatter implementation (i.e. not a Node module) then you should ensure it's a relative path starting with ./
.
The parallel mode previously used problematic "master"/"slave" naming that we've dropped in favour of "coordinator" and "worker". This is mostly an internal detail, but is also reflected in the names of some environment variables you might be using:
CUCUMBER_TOTAL_SLAVES
is nowCUCUMBER_TOTAL_WORKERS
CUCUMBER_SLAVE_ID
is nowCUCUMBER_WORKER_ID
(You can skip this part if you don't use TypeScript in your projects.)
Where before we relied on the community-authored @types/cucumber
package, Cucumber is now built with TypeScript and as such includes its own typings, so you can drop your dependency on the separate package:
$ npm rm @types/cucumber
There are a few minor differences to be aware of:
- The type for data tables was named
TableDefinition
- it's now namedDataTable
World
was typed as an interface, but it's actually a class - you shouldextend
it when building a custom formatter
Also, your tsconfig.json
should have the resolveJsonModule
compiler option switched on. Other than that, a pretty standard TypeScript setup should work as expected.
You can no longer call setDefaultTimeout
from within other support code e.g. a step, hook or your World class; it should be called globally.