From 936f04c35fbfc5721f28c3b32be634cca34c51fd Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 17 Jul 2022 10:36:44 +0100 Subject: [PATCH 01/16] use is-installed-globally, emit warning --- package-lock.json | 110 +++++++++++++++++++++++++++++------ package.json | 4 +- src/cli/index.ts | 2 +- src/cli/install_validator.ts | 34 ++--------- 4 files changed, 98 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index 221f280bc..29dbe767a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,13 +30,13 @@ "glob": "^7.1.6", "has-ansi": "^4.0.1", "indent-string": "^4.0.0", + "is-installed-globally": "^0.4.0", "is-stream": "^2.0.0", "knuth-shuffle-seeded": "^1.0.6", "lodash.merge": "^4.6.2", "lodash.mergewith": "^4.6.2", "mz": "^2.7.0", "progress": "^2.0.3", - "resolve": "^1.19.0", "resolve-pkg": "^2.0.0", "semver": "7.3.7", "stack-chain": "^2.0.0", @@ -70,7 +70,6 @@ "@types/mz": "2.7.4", "@types/node": "16.11.42", "@types/progress": "2.0.5", - "@types/resolve": "1.20.2", "@types/semver": "7.3.10", "@types/sinon-chai": "3.2.8", "@types/sinonjs__fake-timers": "8.1.2", @@ -93,6 +92,7 @@ "express": "4.18.1", "fs-extra": "10.1.0", "genversion": "3.1.1", + "global-dirs": "^3.0.0", "mocha": "10.0.0", "mustache": "4.2.0", "nyc": "15.1.0", @@ -1437,12 +1437,6 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, - "node_modules/@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "dev": true - }, "node_modules/@types/semver": { "version": "7.3.10", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.10.tgz", @@ -3827,7 +3821,8 @@ "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "node_modules/functional-red-black-tree": { "version": "1.0.1", @@ -3957,6 +3952,20 @@ "node": ">=10.13.0" } }, + "node_modules/global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dependencies": { + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globals": { "version": "13.15.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", @@ -4011,6 +4020,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "dependencies": { "function-bind": "^1.1.1" }, @@ -4226,6 +4236,14 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "engines": { + "node": ">=10" + } + }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -4329,6 +4347,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, "dependencies": { "has": "^1.0.3" }, @@ -4380,6 +4399,21 @@ "node": ">=0.10.0" } }, + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -4416,6 +4450,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -5839,7 +5881,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-platform": { "version": "0.11.15", @@ -6422,6 +6465,7 @@ "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, "dependencies": { "is-core-module": "^2.8.1", "path-parse": "^1.0.7", @@ -7020,6 +7064,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -8852,12 +8897,6 @@ "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", "dev": true }, - "@types/resolve": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", - "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", - "dev": true - }, "@types/semver": { "version": "7.3.10", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.10.tgz", @@ -10675,7 +10714,8 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -10768,6 +10808,14 @@ "is-glob": "^4.0.3" } }, + "global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "requires": { + "ini": "2.0.0" + } + }, "globals": { "version": "13.15.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz", @@ -10807,6 +10855,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, "requires": { "function-bind": "^1.1.1" } @@ -10960,6 +11009,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==" + }, "internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -11033,6 +11087,7 @@ "version": "2.9.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, "requires": { "has": "^1.0.3" } @@ -11066,6 +11121,15 @@ "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + } + }, "is-negative-zero": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", @@ -11087,6 +11151,11 @@ "has-tostringtag": "^1.0.0" } }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" + }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -12181,7 +12250,8 @@ "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "path-platform": { "version": "0.11.15", @@ -12603,6 +12673,7 @@ "version": "1.22.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "dev": true, "requires": { "is-core-module": "^2.8.1", "path-parse": "^1.0.7", @@ -13056,7 +13127,8 @@ "supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true }, "test-exclude": { "version": "6.0.0", diff --git a/package.json b/package.json index b08054b60..9e813d491 100644 --- a/package.json +++ b/package.json @@ -215,13 +215,13 @@ "glob": "^7.1.6", "has-ansi": "^4.0.1", "indent-string": "^4.0.0", + "is-installed-globally": "^0.4.0", "is-stream": "^2.0.0", "knuth-shuffle-seeded": "^1.0.6", "lodash.merge": "^4.6.2", "lodash.mergewith": "^4.6.2", "mz": "^2.7.0", "progress": "^2.0.3", - "resolve": "^1.19.0", "resolve-pkg": "^2.0.0", "semver": "7.3.7", "stack-chain": "^2.0.0", @@ -252,7 +252,6 @@ "@types/mz": "2.7.4", "@types/node": "16.11.42", "@types/progress": "2.0.5", - "@types/resolve": "1.20.2", "@types/semver": "7.3.10", "@types/sinon-chai": "3.2.8", "@types/sinonjs__fake-timers": "8.1.2", @@ -275,6 +274,7 @@ "express": "4.18.1", "fs-extra": "10.1.0", "genversion": "3.1.1", + "global-dirs": "^3.0.0", "mocha": "10.0.0", "mustache": "4.2.0", "nyc": "15.1.0", diff --git a/src/cli/index.ts b/src/cli/index.ts index 614a08222..98d5d89a7 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -38,7 +38,7 @@ export default class Cli { } async run(): Promise { - await validateInstall(this.cwd) + await validateInstall() const { options, configuration: argvConfiguration } = ArgvParser.parse( this.argv ) diff --git a/src/cli/install_validator.ts b/src/cli/install_validator.ts index 6396dd5ca..06bfa5a92 100644 --- a/src/cli/install_validator.ts +++ b/src/cli/install_validator.ts @@ -1,39 +1,13 @@ -import fs from 'mz/fs' -import path from 'path' -import resolve from 'resolve' -import { promisify } from 'util' +import isInstalledGlobally from 'is-installed-globally' -export async function validateInstall(cwd: string): Promise { - const projectPath = path.join(__dirname, '..', '..') - if (projectPath === cwd) { - return // cucumber testing itself - } - const currentCucumberPath = require.resolve(projectPath) - let localCucumberPath: string - try { - localCucumberPath = await promisify(resolve)( - '@cucumber/cucumber', - { - basedir: cwd, - } - ) - } catch (e) { - throw new Error( - '`@cucumber/cucumber` module not resolvable. Must be locally installed.' - ) - } - localCucumberPath = await fs.realpath(localCucumberPath) - if (localCucumberPath !== currentCucumberPath) { - throw new Error( +export async function validateInstall(): Promise { + if (isInstalledGlobally) + console.warn( ` You appear to be executing an install of cucumber (most likely a global install) that is different from your local install (the one required in your support files). For cucumber to work, you need to execute the same install that is required in your support files. Please execute the locally installed version to run your tests. - - Executed Path: ${currentCucumberPath} - Local Path: ${localCucumberPath} ` ) - } } From e00a9258ff9741d7c33d3b682aa817d563e94140 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 17 Jul 2022 10:40:19 +0100 Subject: [PATCH 02/16] disable lint rule --- src/cli/install_validator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cli/install_validator.ts b/src/cli/install_validator.ts index 06bfa5a92..ac2ad619c 100644 --- a/src/cli/install_validator.ts +++ b/src/cli/install_validator.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import isInstalledGlobally from 'is-installed-globally' export async function validateInstall(): Promise { From a0c2064698859a277bad31c574d4c71e7a7b47ea Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 17 Jul 2022 13:15:17 +0100 Subject: [PATCH 03/16] remove unused dependency --- package-lock.json | 1 - package.json | 1 - 2 files changed, 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29dbe767a..e08b47295 100644 --- a/package-lock.json +++ b/package-lock.json @@ -92,7 +92,6 @@ "express": "4.18.1", "fs-extra": "10.1.0", "genversion": "3.1.1", - "global-dirs": "^3.0.0", "mocha": "10.0.0", "mustache": "4.2.0", "nyc": "15.1.0", diff --git a/package.json b/package.json index 9e813d491..2f66a3382 100644 --- a/package.json +++ b/package.json @@ -274,7 +274,6 @@ "express": "4.18.1", "fs-extra": "10.1.0", "genversion": "3.1.1", - "global-dirs": "^3.0.0", "mocha": "10.0.0", "mustache": "4.2.0", "nyc": "15.1.0", From 9f6f6eeb2631070372ad0a67075a84b21da21c5a Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 17 Jul 2022 13:18:36 +0100 Subject: [PATCH 04/16] rough implementation of runtime check --- features/global_install.feature | 10 +++++--- src/support_code_library_builder/index.ts | 30 +++++++++++++++++++++-- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/features/global_install.feature b/features/global_install.feature index b34f049b9..8652c9ae4 100644 --- a/features/global_install.feature +++ b/features/global_install.feature @@ -18,10 +18,12 @@ Feature: Global Installs Then it fails And the error output contains the text: """ - You appear to be executing an install of cucumber (most likely a global install) - that is different from your local install (the one required in your support files). - For cucumber to work, you need to execute the same install that is required in your support files. - Please execute the locally installed version to run your tests. + You're calling functions on an instance of Cucumber that isn't running. + This is 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/faq.md """ When I run cucumber-js (installed locally) Then it passes diff --git a/src/support_code_library_builder/index.ts b/src/support_code_library_builder/index.ts index 38ee3e58e..09768296e 100644 --- a/src/support_code_library_builder/index.ts +++ b/src/support_code_library_builder/index.ts @@ -16,7 +16,7 @@ import { ParameterTypeRegistry, RegularExpression, } from '@cucumber/cucumber-expressions' -import { doesHaveValue } from '../value_checker' +import { doesHaveValue, doesNotHaveValue } from '../value_checker' import { DefineStepPattern, IDefineStepOptions, @@ -100,7 +100,7 @@ export class SupportCodeLibraryBuilder { private parallelCanAssign: ParallelAssignmentValidator constructor() { - this.methods = { + const methods: IDefineSupportCodeMethods = { After: this.defineTestCaseHook( () => this.afterTestCaseHookDefinitionConfigs ), @@ -140,6 +140,32 @@ export class SupportCodeLibraryBuilder { Then: this.defineStep('Then', () => this.stepDefinitionConfigs), When: this.defineStep('When', () => this.stepDefinitionConfigs), } + const check = () => { + if (doesNotHaveValue(this.cwd)) { + throw new Error( + ` + You're calling functions on an instance of Cucumber that isn't running. + This is 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/faq.md + ` + ) + } + } + this.methods = new Proxy(methods, { + get( + target: IDefineSupportCodeMethods, + method: keyof IDefineSupportCodeMethods + ): any { + return (...args: any[]) => { + check() + // @ts-expect-error need to fix argument types + return target[method](...args) + } + }, + }) } defineParameterType(options: IParameterTypeDefinition): void { From 21f945939010a1f1dfc85839d8e947110605d21b Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 17 Jul 2022 13:21:02 +0100 Subject: [PATCH 05/16] update comment --- src/support_code_library_builder/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/support_code_library_builder/index.ts b/src/support_code_library_builder/index.ts index 09768296e..a5f3547d4 100644 --- a/src/support_code_library_builder/index.ts +++ b/src/support_code_library_builder/index.ts @@ -161,7 +161,7 @@ export class SupportCodeLibraryBuilder { ): any { return (...args: any[]) => { check() - // @ts-expect-error need to fix argument types + // @ts-expect-error difficult to type this correctly return target[method](...args) } }, From 339a8162a58bc5a47bbe7b5e60f967182c5b5f60 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sun, 17 Jul 2022 13:23:59 +0100 Subject: [PATCH 06/16] include method name you called --- features/global_install.feature | 2 +- src/support_code_library_builder/index.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/features/global_install.feature b/features/global_install.feature index 8652c9ae4..e6c914245 100644 --- a/features/global_install.feature +++ b/features/global_install.feature @@ -18,7 +18,7 @@ Feature: Global Installs Then it fails And the error output contains the text: """ - You're calling functions on an instance of Cucumber that isn't running. + You're calling functions (e.g. "When") on an instance of Cucumber that isn't running. This is mostly likely due to: - Cucumber being installed globally - A project structure where your support code is depending on a different instance of Cucumber diff --git a/src/support_code_library_builder/index.ts b/src/support_code_library_builder/index.ts index a5f3547d4..21f1910dc 100644 --- a/src/support_code_library_builder/index.ts +++ b/src/support_code_library_builder/index.ts @@ -140,11 +140,11 @@ export class SupportCodeLibraryBuilder { Then: this.defineStep('Then', () => this.stepDefinitionConfigs), When: this.defineStep('When', () => this.stepDefinitionConfigs), } - const check = () => { + const checkInstall = (method: string) => { if (doesNotHaveValue(this.cwd)) { throw new Error( ` - You're calling functions on an instance of Cucumber that isn't running. + You're calling functions (e.g. "${method}") on an instance of Cucumber that isn't running. This is mostly likely due to: - Cucumber being installed globally - A project structure where your support code is depending on a different instance of Cucumber @@ -160,7 +160,7 @@ export class SupportCodeLibraryBuilder { method: keyof IDefineSupportCodeMethods ): any { return (...args: any[]) => { - check() + checkInstall(method) // @ts-expect-error difficult to type this correctly return target[method](...args) } From 3f9155609ddf04a94a027e2080453cd500edeb17 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 18 Jul 2022 08:51:24 +0100 Subject: [PATCH 07/16] tweak wording of runtime error --- features/global_install.feature | 2 +- src/support_code_library_builder/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/global_install.feature b/features/global_install.feature index e6c914245..e0ffb5b6a 100644 --- a/features/global_install.feature +++ b/features/global_install.feature @@ -19,7 +19,7 @@ Feature: Global Installs And the error output contains the text: """ You're calling functions (e.g. "When") on an instance of Cucumber that isn't running. - This is mostly likely due to: + 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. diff --git a/src/support_code_library_builder/index.ts b/src/support_code_library_builder/index.ts index 21f1910dc..0793ad558 100644 --- a/src/support_code_library_builder/index.ts +++ b/src/support_code_library_builder/index.ts @@ -145,7 +145,7 @@ export class SupportCodeLibraryBuilder { throw new Error( ` You're calling functions (e.g. "${method}") on an instance of Cucumber that isn't running. - This is mostly likely due to: + 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. From 4cf66978a94f2a1c34f2eeabd8c33d0d9bfee537 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 18 Jul 2022 08:52:59 +0100 Subject: [PATCH 08/16] tweak global message --- src/cli/install_validator.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cli/install_validator.ts b/src/cli/install_validator.ts index ac2ad619c..bddb7d1a4 100644 --- a/src/cli/install_validator.ts +++ b/src/cli/install_validator.ts @@ -5,10 +5,9 @@ export async function validateInstall(): Promise { if (isInstalledGlobally) console.warn( ` - You appear to be executing an install of cucumber (most likely a global install) - that is different from your local install (the one required in your support files). - For cucumber to work, you need to execute the same install that is required in your support files. - Please execute the locally installed version to run your tests. + It looks like you're running Cucumber from a global installation. + This won't work; you need to have Cucumber installed as a local dependency in your project. + See https://github.com/cucumber/cucumber-js/blob/main/docs/faq.md ` ) } From 34df7397a6e96ef9f1a0985edac0c0451d276081 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 18 Jul 2022 09:42:20 +0100 Subject: [PATCH 09/16] expand documentation --- README.md | 1 + docs/cli.md | 3 -- docs/faq.md | 23 +------------- docs/installation.md | 72 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 25 deletions(-) create mode 100644 docs/installation.md diff --git a/README.md b/README.md index 9f2860790..a0ae439a4 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,7 @@ If you learn best by example, we have [a repo with several example projects](htt The following documentation is for `main`, which might contain some unreleased features. See [documentation for older versions](./docs/older_versions.md) if you need it. +* [Installation](./docs/installation.md) * [CLI](./docs/cli.md) * [Configuration](./docs/configuration.md) * Support Code diff --git a/docs/cli.md b/docs/cli.md index e33381cba..6c5bdeeca 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -22,9 +22,6 @@ Or via [npx](https://docs.npmjs.com/cli/v8/commands/npx): $ npx cucumber-js ``` -**Note on global installs:** Cucumber does not work when installed globally because `@cucumber/cucumber` -needs to be required in your support files and globally installed modules cannot be required. - ## Options All the [standard configuration options](./configuration.md#options) can be provided via the CLI. diff --git a/docs/faq.md b/docs/faq.md index 530a6602d..1a8a79734 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -17,25 +17,4 @@ If you have similar `Given` and `Then` patterns, try adding the word “should ## Why am I seeing `The "from" argument must be of type string. Received type undefined`? -If when running cucumber-js you see an error with a stack trace like: - -``` -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 usually an effect of one of: - -- Your project depends on cucumber-js, and also has a dependency (in `node_modules`) that depends on cucumber-js at a different version -- You have a package that depends (even as a dev dependency) on cucumber-js linked (via `npm link` or `yarn link`) - -These cases can cause two different instances of cucumber-js to be in play at runtime, which causes errors. - -If removing the duplicate dependency is not possible, you can work around this by using [import-cwd](https://www.npmjs.com/package/import-cwd) so your support code always requires cucumber-js from the current working directory (i.e. your host project): - -```js -const importCwd = require('import-cwd') -const { Given, When, Then } = importCwd('@cucumber/cucumber') -``` +See [Invalid installations](./installation.md#invalid-installations) diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 000000000..f1abda1bd --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,72 @@ +# 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 most common where you have split some of your support code (e.g. step definitions) into a separate package for reuse across multiple projects. + +You can diagnose this by running `npm why @cucumber/cucumber` in your project. You might see something like: + +``` +@cucumber/cucumber@8.4.0 dev +node_modules/@cucumber/cucumber + dev @cucumber/cucumber@"8.4.0" from the root project + +@cucumber/cucumber@8.3.0 dev +node_modules/my-shared-steps-library/node_modules/@cucumber/cucumber + dev @cucumber/cucumber@"8.3.0" from my-shared-steps-library@1.0.0 + 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 your 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. + +### 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 the 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) + ... +``` From 7089497dfd5d59fbec8851eeeb519146cbeff51d Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 18 Jul 2022 09:44:42 +0100 Subject: [PATCH 10/16] update link to docs --- features/global_install.feature | 2 +- src/cli/install_validator.ts | 2 +- src/support_code_library_builder/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/features/global_install.feature b/features/global_install.feature index e0ffb5b6a..9dfaa3d9e 100644 --- a/features/global_install.feature +++ b/features/global_install.feature @@ -23,7 +23,7 @@ Feature: Global Installs - 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/faq.md + See https://github.com/cucumber/cucumber-js/blob/main/docs/installation.md#invalid-installations """ When I run cucumber-js (installed locally) Then it passes diff --git a/src/cli/install_validator.ts b/src/cli/install_validator.ts index bddb7d1a4..041f36fa0 100644 --- a/src/cli/install_validator.ts +++ b/src/cli/install_validator.ts @@ -7,7 +7,7 @@ export async function validateInstall(): Promise { ` It looks like you're running Cucumber from a global installation. This won't work; you need to have Cucumber installed as a local dependency in your project. - See https://github.com/cucumber/cucumber-js/blob/main/docs/faq.md + See https://github.com/cucumber/cucumber-js/blob/main/docs/installation.md#invalid-installations ` ) } diff --git a/src/support_code_library_builder/index.ts b/src/support_code_library_builder/index.ts index 0793ad558..d311c573c 100644 --- a/src/support_code_library_builder/index.ts +++ b/src/support_code_library_builder/index.ts @@ -149,7 +149,7 @@ export class SupportCodeLibraryBuilder { - 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/faq.md + See https://github.com/cucumber/cucumber-js/blob/main/docs/installation.md#invalid-installations ` ) } From a3e690640b2932c78d287d7f4aca33a325013bb1 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 18 Jul 2022 09:45:51 +0100 Subject: [PATCH 11/16] tweak wording again --- src/cli/install_validator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/install_validator.ts b/src/cli/install_validator.ts index 041f36fa0..94b2136e4 100644 --- a/src/cli/install_validator.ts +++ b/src/cli/install_validator.ts @@ -6,7 +6,7 @@ export async function validateInstall(): Promise { console.warn( ` It looks like you're running Cucumber from a global installation. - This won't work; you need to have Cucumber installed as a local dependency in your project. + This won't work - you need to have Cucumber installed as a local dependency in your project. See https://github.com/cucumber/cucumber-js/blob/main/docs/installation.md#invalid-installations ` ) From 73275476c513c4bf383900dbc681cce8626a4e35 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 18 Jul 2022 11:57:42 +0100 Subject: [PATCH 12/16] rework testing a bit --- ...l.feature => invalid_installation.feature} | 11 ++-- features/step_definitions/cli_steps.ts | 16 ------ features/step_definitions/install_steps.ts | 56 +++++++++++++++++++ features/support/hooks.ts | 53 ------------------ features/support/world.ts | 1 - 5 files changed, 61 insertions(+), 76 deletions(-) rename features/{global_install.feature => invalid_installation.feature} (80%) create mode 100644 features/step_definitions/install_steps.ts diff --git a/features/global_install.feature b/features/invalid_installation.feature similarity index 80% rename from features/global_install.feature rename to features/invalid_installation.feature index 9dfaa3d9e..9f65be4d3 100644 --- a/features/global_install.feature +++ b/features/invalid_installation.feature @@ -1,7 +1,8 @@ -Feature: Global Installs +Feature: Invalid installations - @spawn @global-install - Scenario: executing cucumber from a global install error + @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 @@ -14,7 +15,7 @@ Feature: Global Installs When(/^a step is passing$/, function() {}) """ - When I run cucumber-js (installed globally) + When I run cucumber-js Then it fails And the error output contains the text: """ @@ -25,5 +26,3 @@ Feature: Global Installs 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 """ - When I run cucumber-js (installed locally) - Then it passes diff --git a/features/step_definitions/cli_steps.ts b/features/step_definitions/cli_steps.ts index 17aec0f0b..c23774d4d 100644 --- a/features/step_definitions/cli_steps.ts +++ b/features/step_definitions/cli_steps.ts @@ -76,22 +76,6 @@ When( } ) -When( - 'I run cucumber-js \\(installed locally\\)', - { timeout: 10000 }, - async function (this: World) { - return await this.run(this.localExecutablePath, []) - } -) - -When( - 'I run cucumber-js \\(installed globally\\)', - { timeout: 10000 }, - async function (this: World) { - return await this.run(this.globalExecutablePath, []) - } -) - Then('it passes', () => {}) // eslint-disable-line @typescript-eslint/no-empty-function Then('it fails', function (this: World) { diff --git a/features/step_definitions/install_steps.ts b/features/step_definitions/install_steps.ts new file mode 100644 index 000000000..6de9e0a89 --- /dev/null +++ b/features/step_definitions/install_steps.ts @@ -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 globalInstallCucumberPath = path.join( + installationNodeModulesPath, + '@cucumber', + 'cucumber' + ) + const itemsToCopy = ['bin', 'lib', 'package.json'] + itemsToCopy.forEach((item) => { + fsExtra.copySync( + path.join(projectPath, item), + path.join(globalInstallCucumberPath, item) + ) + }) + + this.localExecutablePath = path.join( + globalInstallCucumberPath, + 'bin', + 'cucumber.js' + ) +}) diff --git a/features/support/hooks.ts b/features/support/hooks.ts index 66d85c3de..e837bf742 100644 --- a/features/support/hooks.ts +++ b/features/support/hooks.ts @@ -1,8 +1,6 @@ import { After, Before, formatterHelpers, ITestCaseHookParameter } from '../../' -import fs from 'fs' import fsExtra from 'fs-extra' import path from 'path' -import tmp from 'tmp' import { doesHaveValue } from '../../src/value_checker' import { World } from './world' import { warnUserAboutEnablingDeveloperMode } from './warn_user_about_enabling_developer_mode' @@ -54,57 +52,6 @@ Before('@esm', function (this: World) { }) }) -Before('@global-install', function (this: World) { - const tmpObject = tmp.dirSync({ unsafeCleanup: true }) - - // Symlink everything in node_modules so the fake global install has all the dependencies it needs - const projectNodeModulesPath = path.join(projectPath, 'node_modules') - const projectNodeModulesDirs = fs.readdirSync(projectNodeModulesPath) - const globalInstallNodeModulesPath = 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 globalInstallNodeModulePath = path.join( - globalInstallNodeModulesPath, - pathToLink - ) - const projectNodeModulePath = path.join( - projectNodeModulesPath, - pathToLink - ) - fsExtra.ensureSymlinkSync( - projectNodeModulePath, - globalInstallNodeModulePath - ) - }) - }) - - const globalInstallCucumberPath = path.join( - globalInstallNodeModulesPath, - '@cucumber', - 'cucumber' - ) - const itemsToCopy = ['bin', 'lib', 'package.json'] - itemsToCopy.forEach((item) => { - fsExtra.copySync( - path.join(projectPath, item), - path.join(globalInstallCucumberPath, item) - ) - }) - - this.globalExecutablePath = path.join( - globalInstallCucumberPath, - 'bin', - 'cucumber.js' - ) -}) - After(async function (this: World) { if (this.reportServer?.started) { await this.reportServer.stop() diff --git a/features/support/world.ts b/features/support/world.ts index ea4e0bde6..e60dbe46f 100644 --- a/features/support/world.ts +++ b/features/support/world.ts @@ -36,7 +36,6 @@ export class World { public lastRun: ILastRun public verifiedLastRunError: boolean public localExecutablePath: string - public globalExecutablePath: string public reportServer: FakeReportServer parseEnvString(str: string): NodeJS.ProcessEnv { From d949de2469137c5a2902f939377d0cd5dc716d15 Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 18 Jul 2022 12:16:02 +0100 Subject: [PATCH 13/16] tweak variable naming --- features/step_definitions/install_steps.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/features/step_definitions/install_steps.ts b/features/step_definitions/install_steps.ts index 6de9e0a89..dfc8335fb 100644 --- a/features/step_definitions/install_steps.ts +++ b/features/step_definitions/install_steps.ts @@ -35,7 +35,7 @@ Given('an invalid installation', async function (this: World) { }) }) - const globalInstallCucumberPath = path.join( + const invalidInstallationCucumberPath = path.join( installationNodeModulesPath, '@cucumber', 'cucumber' @@ -44,12 +44,12 @@ Given('an invalid installation', async function (this: World) { itemsToCopy.forEach((item) => { fsExtra.copySync( path.join(projectPath, item), - path.join(globalInstallCucumberPath, item) + path.join(invalidInstallationCucumberPath, item) ) }) this.localExecutablePath = path.join( - globalInstallCucumberPath, + invalidInstallationCucumberPath, 'bin', 'cucumber.js' ) From 8d06953161c688861122db97a29a5bdbd10798da Mon Sep 17 00:00:00 2001 From: David Goss Date: Mon, 18 Jul 2022 12:24:25 +0100 Subject: [PATCH 14/16] update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4beed015..0019c4cee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) on how to contribute to Cucumber. ## [Unreleased] +### Changed +- Reworked handling for invalid installations ([#2089](https://github.com/cucumber/cucumber-js/pull/2089)) ## [8.4.0] - 2022-06-29 ### Fixed From a2d6d1591aff440619f40fb4f9e755be65b5a959 Mon Sep 17 00:00:00 2001 From: David Goss Date: Tue, 19 Jul 2022 01:01:33 +0100 Subject: [PATCH 15/16] address review comments --- docs/installation.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index f1abda1bd..5dd4d5a03 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -36,7 +36,7 @@ We'll emit a warning if it looks like Cucumber is installed globally. 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 most common where you have split some of your support code (e.g. step definitions) into a separate package for reuse across multiple projects. +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: @@ -52,7 +52,11 @@ node_modules/my-shared-steps-library/node_modules/@cucumber/cucumber my-shared-steps-library@"1.0.0" from the root project ``` -In this case, the fix is to change your 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. +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 From 87cab4e155ef912a911a63f8bd7c1dc24eadeb61 Mon Sep 17 00:00:00 2001 From: David Goss Date: Tue, 19 Jul 2022 08:55:43 +0100 Subject: [PATCH 16/16] fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Aurélien Reeves --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 5dd4d5a03..1da91bbeb 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -62,7 +62,7 @@ When looking at the duplicate dependency issue, it's worth checking whether anyt 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 the 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). +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