-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rework handling and docs for invalid installations (#2089)
* use is-installed-globally, emit warning * disable lint rule * remove unused dependency * rough implementation of runtime check * update comment * include method name you called * tweak wording of runtime error * tweak global message * expand documentation * update link to docs * tweak wording again * rework testing a bit * tweak variable naming * update CHANGELOG.md * address review comments * fix typo Co-authored-by: Aurélien Reeves <[email protected]> Co-authored-by: Aurélien Reeves <[email protected]>
- Loading branch information
1 parent
88de2e8
commit c4697e6
Showing
16 changed files
with
292 additions
and
180 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
# Installation | ||
|
||
With [npm](https://www.npmjs.com/): | ||
|
||
```shell | ||
$ npm install @cucumber/cucumber | ||
``` | ||
|
||
With [Yarn](https://yarnpkg.com/): | ||
|
||
```shell | ||
$ yarn add @cucumber/cucumber | ||
``` | ||
|
||
## Invalid installations | ||
|
||
If Cucumber exits with an error message like: | ||
|
||
``` | ||
You're calling functions (e.g. "Given") on an instance of Cucumber that isn't running. | ||
This means you have an invalid installation, mostly likely due to: | ||
... | ||
``` | ||
|
||
This means you have an invalid installation. | ||
|
||
Unlike many libraries, Cucumber is _stateful_; you call functions to register your support code, and we keep that state until it's used in the test run. Therefore, it's important that everything interacting with Cucumber in your project is interacting with the same instance. There are a few ways this can go wrong: | ||
|
||
### Global installation | ||
|
||
Some libraries with a command-line interface are designed to be installed globally. Not Cucumber though - for the reasons above, you need to install it as a dependency in your project. | ||
|
||
We'll emit a warning if it looks like Cucumber is installed globally. | ||
|
||
### Duplicate dependency | ||
|
||
If your project depends on `@cucumber/cucumber`, but also has another dependency that _itself_ depends on `@cucumber/cucumber` (maybe at a slightly different version), this can cause the issue with multiple instances in play at the same time. If you're familiar with React, this is a lot like [the "invalid hook call" issue](https://reactjs.org/warnings/invalid-hook-call-warning.html#duplicate-react). | ||
|
||
This is common where you have split some of your support code (e.g. step definitions) into a separate package for reuse across multiple projects, or are perhaps using a third-party package intended to work with Cucumber. | ||
|
||
You can diagnose this by running `npm why @cucumber/cucumber` in your project. You might see something like: | ||
|
||
``` | ||
@cucumber/[email protected] dev | ||
node_modules/@cucumber/cucumber | ||
dev @cucumber/cucumber@"8.4.0" from the root project | ||
@cucumber/[email protected] dev | ||
node_modules/my-shared-steps-library/node_modules/@cucumber/cucumber | ||
dev @cucumber/cucumber@"8.3.0" from [email protected] | ||
node_modules/my-shared-steps-library | ||
my-shared-steps-library@"1.0.0" from the root project | ||
``` | ||
|
||
In this case, the fix is to change the library so `@cucumber/cucumber` is a [peer dependency](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#peerdependencies) rather than a regular dependency (it probably also needs to be a dev dependency). This will remove the duplication in the host project. If you don't control the library, consider using [overrides](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides) (npm) or [resolutions](https://classic.yarnpkg.com/lang/en/docs/selective-version-resolutions/) (Yarn) to get it down to a single instance. | ||
|
||
### Deprecated package | ||
|
||
When looking at the duplicate dependency issue, it's worth checking whether anything in your project is depending on the old, deprecated `cucumber` package. Anything touching Cucumber [should be using](../UPGRADING.md#package-name) the newer `@cucumber/cucumber` package. | ||
|
||
### Linking | ||
|
||
With the shared library example above, even if you have `@cucumber/cucumber` correctly defined as a peer dependency, you can still hit the issue if you hook up the library locally using `npm link` or `yarn link` when developing or testing. | ||
|
||
This is trickier to deal with. If you run `npm link ../my-project/node_modules/@cucumber/cucumber` from the library, this should work around it (assuming `my-project` is your host project's directory, and it's adjacent to your library in the file system). | ||
|
||
### Notes | ||
|
||
In earlier versions of Cucumber, this issue would present with a more cryptic error (the causes and solutions are the same): | ||
|
||
``` | ||
TypeError [ERR_INVALID_ARG_TYPE]: The "from" argument must be of type string. Received type undefined | ||
at validateString (internal/validators.js:125:11) | ||
at Object.relative (path.js:1162:5) | ||
... | ||
``` |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
Feature: Invalid installations | ||
|
||
@spawn | ||
Scenario: Cucumber exits with an error when running an invalid installation | ||
Given an invalid installation | ||
Given a file named "features/a.feature" with: | ||
""" | ||
Feature: some feature | ||
Scenario: | ||
When a step is passing | ||
""" | ||
And a file named "features/step_definitions/cucumber_steps.js" with: | ||
""" | ||
const {When} = require('@cucumber/cucumber') | ||
When(/^a step is passing$/, function() {}) | ||
""" | ||
When I run cucumber-js | ||
Then it fails | ||
And the error output contains the text: | ||
""" | ||
You're calling functions (e.g. "When") on an instance of Cucumber that isn't running. | ||
This means you have an invalid installation, mostly likely due to: | ||
- Cucumber being installed globally | ||
- A project structure where your support code is depending on a different instance of Cucumber | ||
Either way, you'll need to address this in order for Cucumber to work. | ||
See https://github.com/cucumber/cucumber-js/blob/main/docs/installation.md#invalid-installations | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { Given } from '../../' | ||
import tmp from 'tmp' | ||
import path from 'path' | ||
import fs from 'fs' | ||
import fsExtra from 'fs-extra' | ||
import { World } from '../support/world' | ||
|
||
/* | ||
Simulates something like a global install, where the Cucumber being executed | ||
is not the one being imported by support code | ||
*/ | ||
Given('an invalid installation', async function (this: World) { | ||
const projectPath = path.join(__dirname, '..', '..') | ||
const tmpObject = tmp.dirSync({ unsafeCleanup: true }) | ||
|
||
// Symlink everything in node_modules so the fake installation has all the dependencies it needs | ||
const projectNodeModulesPath = path.join(projectPath, 'node_modules') | ||
const projectNodeModulesDirs = fs.readdirSync(projectNodeModulesPath) | ||
const installationNodeModulesPath = path.join(tmpObject.name, 'node_modules') | ||
projectNodeModulesDirs.forEach((nodeModuleDir) => { | ||
let pathsToLink = [nodeModuleDir] | ||
if (nodeModuleDir[0] === '@') { | ||
const scopeNodeModuleDirs = fs.readdirSync( | ||
path.join(projectNodeModulesPath, nodeModuleDir) | ||
) | ||
pathsToLink = scopeNodeModuleDirs.map((x) => path.join(nodeModuleDir, x)) | ||
} | ||
pathsToLink.forEach((pathToLink) => { | ||
const installationPackagePath = path.join( | ||
installationNodeModulesPath, | ||
pathToLink | ||
) | ||
const projectPackagePath = path.join(projectNodeModulesPath, pathToLink) | ||
fsExtra.ensureSymlinkSync(projectPackagePath, installationPackagePath) | ||
}) | ||
}) | ||
|
||
const invalidInstallationCucumberPath = path.join( | ||
installationNodeModulesPath, | ||
'@cucumber', | ||
'cucumber' | ||
) | ||
const itemsToCopy = ['bin', 'lib', 'package.json'] | ||
itemsToCopy.forEach((item) => { | ||
fsExtra.copySync( | ||
path.join(projectPath, item), | ||
path.join(invalidInstallationCucumberPath, item) | ||
) | ||
}) | ||
|
||
this.localExecutablePath = path.join( | ||
invalidInstallationCucumberPath, | ||
'bin', | ||
'cucumber.js' | ||
) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.