diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14f362b45e305..78c2926afd6ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [10.x, 12.x, 14.x] + node-version: [10.x, 12.x, 14.x, 16.x] platform: - os: ubuntu-latest shell: bash @@ -83,7 +83,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: ['10.1', 10.x, '12.1', 12.x, '14.1', 14.x] + node-version: ['10.1', 10.x, '12.1', 12.x, '14.1', 14.x, '16.1', 16.x] platform: - os: ubuntu-latest shell: bash diff --git a/AUTHORS b/AUTHORS index f7e19d0c26b09..a8dfd8b6be682 100644 --- a/AUTHORS +++ b/AUTHORS @@ -782,3 +782,4 @@ Nariyasu Heseri rethab Spencer Wilson <5624115+spencerwilson@users.noreply.github.com> Daniel Park +Daniel Park diff --git a/CHANGELOG.md b/CHANGELOG.md index def5daee027f2..c86373bcde2b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +## v7.15.1 (2021-05-31) + +### BUG FIXES + +* [`598a17a26`](https://github.com/npm/cli/commit/598a17a2671c9e3bc204dddd6488169c9a72c6a1) + [#3329](https://github.com/npm/cli/issues/3329) + fix(libnpmexec): don't detach output from npm + ([@wraithgar](https://github.com/wraithgar)) + +### DEPENDENCIES + +* [`c4fc03e9e`](https://github.com/npm/cli/commit/c4fc03e9eb3a6386e8feacb67c19f0a1578dfe38) + `@npmcli/arborist@2.6.1` + * fixes reifying deps with mismatching version ranges between + actual and virtual trees +* [`9159fa62a`](https://github.com/npm/cli/commit/9159fa62a10dee09daef178fc7be161a02824004) + `libnpmexec@1.2.0` + ## v7.15.0 (2021-05-27) ### FEATURES diff --git a/lib/diff.js b/lib/diff.js index 1eaceb4f2f61f..58834ca9c2674 100644 --- a/lib/diff.js +++ b/lib/diff.js @@ -8,7 +8,7 @@ const npmlog = require('npmlog') const pacote = require('pacote') const pickManifest = require('npm-pick-manifest') -const readLocalPkg = require('./utils/read-local-package.js') +const readPackageName = require('./utils/read-package-name.js') const BaseCommand = require('./base-command.js') class Diff extends BaseCommand { @@ -97,7 +97,7 @@ class Diff extends BaseCommand { let noPackageJson let pkgName try { - pkgName = await readLocalPkg(this.npm) + pkgName = await readPackageName(this.npm.prefix) } catch (e) { npmlog.verbose('diff', 'could not read project dir package.json') noPackageJson = true @@ -120,7 +120,7 @@ class Diff extends BaseCommand { let noPackageJson let pkgName try { - pkgName = await readLocalPkg(this.npm) + pkgName = await readPackageName(this.npm.prefix) } catch (e) { npmlog.verbose('diff', 'could not read project dir package.json') noPackageJson = true @@ -238,7 +238,7 @@ class Diff extends BaseCommand { if (semverA && semverB) { let pkgName try { - pkgName = await readLocalPkg(this.npm) + pkgName = await readPackageName(this.npm.prefix) } catch (e) { npmlog.verbose('diff', 'could not read project dir package.json') } diff --git a/lib/dist-tag.js b/lib/dist-tag.js index 64e8abc013745..11b1ad931e18b 100644 --- a/lib/dist-tag.js +++ b/lib/dist-tag.js @@ -4,7 +4,7 @@ const regFetch = require('npm-registry-fetch') const semver = require('semver') const otplease = require('./utils/otplease.js') -const readLocalPkgName = require('./utils/read-local-package.js') +const readPackageName = require('./utils/read-package-name.js') const getWorkspaces = require('./workspaces/get-workspaces.js') const BaseCommand = require('./base-command.js') @@ -64,7 +64,7 @@ class DistTag extends BaseCommand { // should be listing the existing tags return this.list(cmdName, opts) } else - throw this.usage + throw this.usageError() } execWorkspaces (args, filters, cb) { @@ -102,7 +102,7 @@ class DistTag extends BaseCommand { log.verbose('dist-tag add', defaultTag, 'to', spec.name + '@' + version) if (!spec.name || !version || !defaultTag) - throw this.usage + throw this.usageError() const t = defaultTag.trim() @@ -135,7 +135,7 @@ class DistTag extends BaseCommand { log.verbose('dist-tag del', tag, 'from', spec.name) if (!spec.name) - throw this.usage + throw this.usageError() const tags = await this.fetchTags(spec, opts) if (!tags[tag]) { @@ -157,9 +157,11 @@ class DistTag extends BaseCommand { async list (spec, opts) { if (!spec) { - const pkg = await readLocalPkgName(this.npm) + if (this.npm.config.get('global')) + throw this.usageError() + const pkg = await readPackageName(this.npm.prefix) if (!pkg) - throw this.usage + throw this.usageError() return this.list(pkg, opts) } diff --git a/lib/exec.js b/lib/exec.js index f30746b8c50ed..8a87615d9749e 100644 --- a/lib/exec.js +++ b/lib/exec.js @@ -76,8 +76,8 @@ class Exec extends BaseCommand { localBin, log, globalBin, - output, } = this.npm + const output = (...outputArgs) => this.npm.output(...outputArgs) const scriptShell = this.npm.config.get('script-shell') || undefined const packages = this.npm.config.get('package') const yes = this.npm.config.get('yes') diff --git a/lib/init.js b/lib/init.js index bc809a15e49a9..4dd091601e191 100644 --- a/lib/init.js +++ b/lib/init.js @@ -113,8 +113,13 @@ class Init extends BaseCommand { localBin, log, globalBin, - output, } = this.npm + // this function is definitely called. But because of coverage map stuff + // it ends up both uncovered, and the coverage report doesn't even mention. + // the tests do assert that some output happens, so we know this line is + // being hit. + /* istanbul ignore next */ + const output = (...outputArgs) => this.npm.output(...outputArgs) const locationMsg = await getLocationMsg({ color, path }) const runPath = path const scriptShell = this.npm.config.get('script-shell') || undefined diff --git a/lib/owner.js b/lib/owner.js index e57d2268d2619..311b25064e638 100644 --- a/lib/owner.js +++ b/lib/owner.js @@ -4,7 +4,7 @@ const npmFetch = require('npm-registry-fetch') const pacote = require('pacote') const otplease = require('./utils/otplease.js') -const readLocalPkg = require('./utils/read-local-package.js') +const readLocalPkgName = require('./utils/read-package-name.js') const BaseCommand = require('./base-command.js') class Owner extends BaseCommand { @@ -47,7 +47,9 @@ class Owner extends BaseCommand { // reaches registry in order to autocomplete rm if (argv[2] === 'rm') { - const pkgName = await readLocalPkg(this.npm) + if (this.npm.config.get('global')) + return [] + const pkgName = await readLocalPkgName(this.npm.prefix) if (!pkgName) return [] @@ -84,7 +86,10 @@ class Owner extends BaseCommand { async ls (pkg, opts) { if (!pkg) { - const pkgName = await readLocalPkg(this.npm) + if (this.npm.config.get('global')) + throw this.usageError() + + const pkgName = await readLocalPkgName(this.npm.prefix) if (!pkgName) throw this.usageError() @@ -113,7 +118,9 @@ class Owner extends BaseCommand { throw this.usageError() if (!pkg) { - const pkgName = await readLocalPkg(this.npm) + if (this.npm.config.get('global')) + throw this.usageError() + const pkgName = await readLocalPkgName(this.npm.prefix) if (!pkgName) throw this.usageError() @@ -131,7 +138,9 @@ class Owner extends BaseCommand { throw this.usageError() if (!pkg) { - const pkgName = await readLocalPkg(this.npm) + if (this.npm.config.get('global')) + throw this.usageError() + const pkgName = await readLocalPkgName(this.npm.prefix) if (!pkgName) throw this.usageError() diff --git a/lib/utils/read-local-package.js b/lib/utils/read-package-name.js similarity index 56% rename from lib/utils/read-local-package.js rename to lib/utils/read-package-name.js index 21506ca180a0f..7ed15987767bb 100644 --- a/lib/utils/read-local-package.js +++ b/lib/utils/read-package-name.js @@ -1,10 +1,7 @@ const { resolve } = require('path') const readJson = require('read-package-json-fast') -async function readLocalPackageName (npm) { - if (npm.config.get('global')) - return - - const filepath = resolve(npm.prefix, 'package.json') +async function readLocalPackageName (prefix) { + const filepath = resolve(prefix, 'package.json') const json = await readJson(filepath) return json.name } diff --git a/node_modules/@npmcli/arborist/lib/diff.js b/node_modules/@npmcli/arborist/lib/diff.js index 842996ba48669..dac7c81f8ecfb 100644 --- a/node_modules/@npmcli/arborist/lib/diff.js +++ b/node_modules/@npmcli/arborist/lib/diff.js @@ -110,16 +110,32 @@ const getAction = ({actual, ideal}) => { if (ideal.isRoot && actual.isRoot) return null + // if the versions don't match, it's a change no matter what + if (ideal.version !== actual.version) + return 'CHANGE' + const binsExist = ideal.binPaths.every((path) => existsSync(path)) // top nodes, links, and git deps won't have integrity, but do have resolved - if (!ideal.integrity && !actual.integrity && ideal.resolved === actual.resolved && binsExist) + // if neither node has integrity, the bins exist, and either (a) neither + // node has a resolved value or (b) they both do and match, then we can + // leave this one alone since we already know the versions match due to + // the condition above. The "neither has resolved" case (a) cannot be + // treated as a 'mark CHANGE and refetch', because shrinkwraps, bundles, + // and link deps may lack this information, and we don't want to try to + // go to the registry for something that isn't there. + const noIntegrity = !ideal.integrity && !actual.integrity + const noResolved = !ideal.resolved && !actual.resolved + const resolvedMatch = ideal.resolved && ideal.resolved === actual.resolved + if (noIntegrity && binsExist && (resolvedMatch || noResolved)) return null // otherwise, verify that it's the same bits // note that if ideal has integrity, and resolved doesn't, we treat // that as a 'change', so that it gets re-fetched and locked down. - if (!ideal.integrity || !actual.integrity || !ssri.parse(ideal.integrity).match(actual.integrity) || !binsExist) + const integrityMismatch = !ideal.integrity || !actual.integrity || + !ssri.parse(ideal.integrity).match(actual.integrity) + if (integrityMismatch || !binsExist) return 'CHANGE' return null diff --git a/node_modules/@npmcli/arborist/lib/shrinkwrap.js b/node_modules/@npmcli/arborist/lib/shrinkwrap.js index cff9f09633dfc..0a19ef93005ad 100644 --- a/node_modules/@npmcli/arborist/lib/shrinkwrap.js +++ b/node_modules/@npmcli/arborist/lib/shrinkwrap.js @@ -714,6 +714,7 @@ class Shrinkwrap { resolved, integrity, hasShrinkwrap, + version, } = this.get(node.path) const pathFixed = !resolved ? null @@ -727,8 +728,12 @@ class Shrinkwrap { node.resolved === pathFixed const integrityOk = !integrity || !node.integrity || node.integrity === integrity + const versionOk = !version || !node.version || version === node.version - if ((resolved || integrity) && resolvedOk && integrityOk) { + const allOk = (resolved || integrity || version) && + resolvedOk && integrityOk && versionOk + + if (allOk) { node.resolved = node.resolved || pathFixed || null node.integrity = node.integrity || integrity || null node.hasShrinkwrap = node.hasShrinkwrap || hasShrinkwrap || false diff --git a/node_modules/@npmcli/arborist/package.json b/node_modules/@npmcli/arborist/package.json index 8500eaadf6752..8aaa8ecdb7a4e 100644 --- a/node_modules/@npmcli/arborist/package.json +++ b/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "2.6.0", + "version": "2.6.1", "description": "Manage node_modules trees", "dependencies": { "@npmcli/installed-package-contents": "^1.0.7", diff --git a/node_modules/libnpmexec/CHANGELOG.md b/node_modules/libnpmexec/CHANGELOG.md new file mode 100644 index 0000000000000..28cb71028868e --- /dev/null +++ b/node_modules/libnpmexec/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +## v1.1.0 + +- Add add walk up dir lookup logic to satisfy local bins, +similar to `@npmcli/run-script` + +## v1.0.1 + +- Fix `scriptShell` option name. + +## v1.0.0 + +- Initial implementation, moves the code that used to live in the **npm cli**, +ref: https://github.com/npm/cli/blob/release/v7.10.0/lib/exec.js into this +separate module, providing a programmatic API to the **npm exec** functionality. + diff --git a/node_modules/libnpmexec/README.md b/node_modules/libnpmexec/README.md index fb7a771760019..18a26011adc76 100644 --- a/node_modules/libnpmexec/README.md +++ b/node_modules/libnpmexec/README.md @@ -39,7 +39,7 @@ await libexec({ - `packages`: A list of packages to be used (possibly fetch from the registry) **Array**, defaults to `[]` - `path`: Location to where to read local project info (`package.json`) **String**, defaults to `.` - `runPath`: Location to where to execute the script **String**, defaults to `.` - - `scriptShell`: Default shell to be used **String** + - `scriptShell`: Default shell to be used **String**, defaults to `sh` on POSIX systems, `process.env.ComSpec` OR `cmd` on Windows - `yes`: Should skip download confirmation prompt when fetching missing packages from the registry? **Boolean** - `registry`, `cache`, and more options that are forwarded to [@npmcli/arborist](https://github.com/npm/arborist/) and [pacote](https://github.com/npm/pacote/#options) **Object** diff --git a/node_modules/libnpmexec/lib/index.js b/node_modules/libnpmexec/lib/index.js index a48c654bf6a4f..8c5181f397519 100644 --- a/node_modules/libnpmexec/lib/index.js +++ b/node_modules/libnpmexec/lib/index.js @@ -16,6 +16,7 @@ const getBinFromManifest = require('./get-bin-from-manifest.js') const manifestMissing = require('./manifest-missing.js') const noTTY = require('./no-tty.js') const runScript = require('./run-script.js') +const isWindows = require('./is-windows.js') /* istanbul ignore next */ const PATH = ( @@ -34,7 +35,7 @@ const exec = async (opts) => { packages: _packages = [], path = '.', runPath = '.', - scriptShell = undefined, + scriptShell = isWindows ? process.env.ComSpec || 'cmd' : 'sh', yes = undefined, ...flatOptions } = opts diff --git a/node_modules/libnpmexec/lib/is-windows.js b/node_modules/libnpmexec/lib/is-windows.js new file mode 100644 index 0000000000000..fbece90ad7496 --- /dev/null +++ b/node_modules/libnpmexec/lib/is-windows.js @@ -0,0 +1 @@ +module.exports = process.platform === 'win32' diff --git a/node_modules/libnpmexec/package.json b/node_modules/libnpmexec/package.json index c113ac6d0a607..2b3b488cf079f 100644 --- a/node_modules/libnpmexec/package.json +++ b/node_modules/libnpmexec/package.json @@ -1,6 +1,6 @@ { "name": "libnpmexec", - "version": "1.1.1", + "version": "1.2.0", "files": [ "lib" ], diff --git a/package-lock.json b/package-lock.json index 3e3f6a7db04c7..ac1cb7e2e0d81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "npm", - "version": "7.15.0", + "version": "7.15.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "npm", - "version": "7.15.0", + "version": "7.15.1", "bundleDependencies": [ "@npmcli/arborist", "@npmcli/ci-detect", @@ -78,7 +78,7 @@ ], "license": "Artistic-2.0", "dependencies": { - "@npmcli/arborist": "^2.6.0", + "@npmcli/arborist": "^2.6.1", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.2.0", "@npmcli/run-script": "^1.8.5", @@ -103,7 +103,7 @@ "leven": "^3.1.0", "libnpmaccess": "^4.0.2", "libnpmdiff": "^2.0.4", - "libnpmexec": "^1.1.1", + "libnpmexec": "^1.2.0", "libnpmfund": "^1.1.0", "libnpmhook": "^6.0.2", "libnpmorg": "^2.0.2", @@ -154,7 +154,7 @@ "@mdx-js/mdx": "^1.6.22", "cmark-gfm": "^0.8.5", "eslint": "^7.26.0", - "eslint-plugin-import": "^2.22.1", + "eslint-plugin-import": "^2.23.4", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^5.0.0", @@ -712,9 +712,9 @@ } }, "node_modules/@npmcli/arborist": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.6.0.tgz", - "integrity": "sha512-6njRVuPMgGRvQUmsXwGdp1ItZtJuSdt5ouoQe4AeFTTZoMufKWLeXFDOlWj7qbMAzqw+guNEAZwBiwm04J7T2g==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.6.1.tgz", + "integrity": "sha512-OOlntFIOAo7RplEQaYXlA5U5NXE+EwZtnTCsit4Wtme5+llGiea6GBytuV8dOzdPMPlNx3fQQjBUE9E8k76yjQ==", "inBundle": true, "dependencies": { "@npmcli/installed-package-contents": "^1.0.7", @@ -2611,9 +2611,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.23.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.3.tgz", - "integrity": "sha512-wDxdYbSB55F7T5CC7ucDjY641VvKmlRwT0Vxh7PkY1mI4rclVRFWYfsrjDgZvwYYDZ5ee0ZtfFKXowWjqvEoRQ==", + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", "dev": true, "dependencies": { "array-includes": "^3.1.3", @@ -4650,9 +4650,9 @@ } }, "node_modules/libnpmexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/libnpmexec/-/libnpmexec-1.1.1.tgz", - "integrity": "sha512-uw6H2dzC6F6fdq7lAxfzXPimHCJ3/g6ycFKcv2Q2QXuNZ94EDmNPpMO6f4mwiC5F6Lyy/WK0IL7AZwRhmSvUdQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/libnpmexec/-/libnpmexec-1.2.0.tgz", + "integrity": "sha512-LkxnH2wsMUI4thsgUK0r+EFZ5iCjKlp21J68dFY7AzD5uaaIPqO3lqVvYbyl1Umz1R4rY9t3vFa1fF3hzo6Y2Q==", "inBundle": true, "dependencies": { "@npmcli/arborist": "^2.3.0", @@ -10811,9 +10811,9 @@ "dev": true }, "@npmcli/arborist": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.6.0.tgz", - "integrity": "sha512-6njRVuPMgGRvQUmsXwGdp1ItZtJuSdt5ouoQe4AeFTTZoMufKWLeXFDOlWj7qbMAzqw+guNEAZwBiwm04J7T2g==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.6.1.tgz", + "integrity": "sha512-OOlntFIOAo7RplEQaYXlA5U5NXE+EwZtnTCsit4Wtme5+llGiea6GBytuV8dOzdPMPlNx3fQQjBUE9E8k76yjQ==", "requires": { "@npmcli/installed-package-contents": "^1.0.7", "@npmcli/map-workspaces": "^1.0.2", @@ -12286,9 +12286,9 @@ } }, "eslint-plugin-import": { - "version": "2.23.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.3.tgz", - "integrity": "sha512-wDxdYbSB55F7T5CC7ucDjY641VvKmlRwT0Vxh7PkY1mI4rclVRFWYfsrjDgZvwYYDZ5ee0ZtfFKXowWjqvEoRQ==", + "version": "2.23.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.23.4.tgz", + "integrity": "sha512-6/wP8zZRsnQFiR3iaPFgh5ImVRM1WN5NUWfTIRqwOdeiGJlBcSk82o1FEVq8yXmy4lkIzTo7YhHCIxlU/2HyEQ==", "dev": true, "requires": { "array-includes": "^3.1.3", @@ -13690,9 +13690,9 @@ } }, "libnpmexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/libnpmexec/-/libnpmexec-1.1.1.tgz", - "integrity": "sha512-uw6H2dzC6F6fdq7lAxfzXPimHCJ3/g6ycFKcv2Q2QXuNZ94EDmNPpMO6f4mwiC5F6Lyy/WK0IL7AZwRhmSvUdQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/libnpmexec/-/libnpmexec-1.2.0.tgz", + "integrity": "sha512-LkxnH2wsMUI4thsgUK0r+EFZ5iCjKlp21J68dFY7AzD5uaaIPqO3lqVvYbyl1Umz1R4rY9t3vFa1fF3hzo6Y2Q==", "requires": { "@npmcli/arborist": "^2.3.0", "@npmcli/ci-detect": "^1.3.0", diff --git a/package.json b/package.json index 60637d6055154..7df43589334ef 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "7.15.0", + "version": "7.15.1", "name": "npm", "description": "a package manager for JavaScript", "keywords": [ @@ -42,7 +42,7 @@ "./package.json": "./package.json" }, "dependencies": { - "@npmcli/arborist": "^2.6.0", + "@npmcli/arborist": "^2.6.1", "@npmcli/ci-detect": "^1.2.0", "@npmcli/config": "^2.2.0", "@npmcli/run-script": "^1.8.5", @@ -67,7 +67,7 @@ "leven": "^3.1.0", "libnpmaccess": "^4.0.2", "libnpmdiff": "^2.0.4", - "libnpmexec": "^1.1.1", + "libnpmexec": "^1.2.0", "libnpmfund": "^1.1.0", "libnpmhook": "^6.0.2", "libnpmorg": "^2.0.2", @@ -183,7 +183,7 @@ "@mdx-js/mdx": "^1.6.22", "cmark-gfm": "^0.8.5", "eslint": "^7.26.0", - "eslint-plugin-import": "^2.22.1", + "eslint-plugin-import": "^2.23.4", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.0", "eslint-plugin-standard": "^5.0.0", diff --git a/smoke-tests/server.js b/smoke-tests/server.js index 03aacb4b8c28a..b864114af64a8 100644 --- a/smoke-tests/server.js +++ b/smoke-tests/server.js @@ -1,3 +1,4 @@ +/* istanbul ignore file */ const {join, dirname} = require('path') const {existsSync, readFileSync, writeFileSync} = require('fs') const PORT = 12345 + (+process.env.TAP_CHILD_ID || 0) diff --git a/tap-snapshots/test/lib/dist-tag.js.test.cjs b/tap-snapshots/test/lib/dist-tag.js.test.cjs index 86a9c84eb1eb6..21d9331db1299 100644 --- a/tap-snapshots/test/lib/dist-tag.js.test.cjs +++ b/tap-snapshots/test/lib/dist-tag.js.test.cjs @@ -6,7 +6,8 @@ */ 'use strict' exports[`test/lib/dist-tag.js TAP add missing args > should exit usage error message 1`] = ` -npm dist-tag +Error: +Usage: npm dist-tag Modify package distribution tags @@ -21,11 +22,14 @@ Options: alias: dist-tags -Run "npm help dist-tag" for more info +Run "npm help dist-tag" for more info { + "code": "EUSAGE", +} ` exports[`test/lib/dist-tag.js TAP add missing pkg name > should exit usage error message 1`] = ` -npm dist-tag +Error: +Usage: npm dist-tag Modify package distribution tags @@ -40,7 +44,9 @@ Options: alias: dist-tags -Run "npm help dist-tag" for more info +Run "npm help dist-tag" for more info { + "code": "EUSAGE", +} ` exports[`test/lib/dist-tag.js TAP add new tag > should return success msg 1`] = ` @@ -53,7 +59,8 @@ dist-tag add 1.0.0 to @scoped/another@7.7.7 ` exports[`test/lib/dist-tag.js TAP borked cmd usage > should show usage error 1`] = ` -npm dist-tag +Error: +Usage: npm dist-tag Modify package distribution tags @@ -68,7 +75,31 @@ Options: alias: dist-tags -Run "npm help dist-tag" for more info +Run "npm help dist-tag" for more info { + "code": "EUSAGE", +} +` + +exports[`test/lib/dist-tag.js TAP ls global > should throw basic usage 1`] = ` +Error: +Usage: npm dist-tag + +Modify package distribution tags + +Usage: +npm dist-tag add @ [] +npm dist-tag rm +npm dist-tag ls [] + +Options: +[-w|--workspace [-w|--workspace ...]] +[-ws|--workspaces] + +alias: dist-tags + +Run "npm help dist-tag" for more info { + "code": "EUSAGE", +} ` exports[`test/lib/dist-tag.js TAP ls in current package > should list available tags for current package 1`] = ` @@ -78,7 +109,8 @@ latest: 1.0.0 ` exports[`test/lib/dist-tag.js TAP ls on missing name in current package > should throw usage error message 1`] = ` -npm dist-tag +Error: +Usage: npm dist-tag Modify package distribution tags @@ -93,7 +125,9 @@ Options: alias: dist-tags -Run "npm help dist-tag" for more info +Run "npm help dist-tag" for more info { + "code": "EUSAGE", +} ` exports[`test/lib/dist-tag.js TAP ls on missing package > should log no dist-tag found msg 1`] = ` @@ -133,7 +167,8 @@ exports[`test/lib/dist-tag.js TAP remove existing tag > should return success ms ` exports[`test/lib/dist-tag.js TAP remove missing pkg name > should exit usage error message 1`] = ` -npm dist-tag +Error: +Usage: npm dist-tag Modify package distribution tags @@ -148,7 +183,9 @@ Options: alias: dist-tags -Run "npm help dist-tag" for more info +Run "npm help dist-tag" for more info { + "code": "EUSAGE", +} ` exports[`test/lib/dist-tag.js TAP remove non-existing tag > should log error msg 1`] = ` diff --git a/tap-snapshots/test/lib/init.js.test.cjs b/tap-snapshots/test/lib/init.js.test.cjs index 043d8b641dcce..95abbe6c1d830 100644 --- a/tap-snapshots/test/lib/init.js.test.cjs +++ b/tap-snapshots/test/lib/init.js.test.cjs @@ -6,13 +6,28 @@ */ 'use strict' exports[`test/lib/init.js TAP workspaces no args > should print helper info 1`] = ` - +Array [ + Array [ + String( + This utility will walk you through creating a package.json file. + It only covers the most common items, and tries to guess sensible defaults. + + See \`npm help init\` for definitive documentation on these fields + and exactly what they do. + + Use \`npm install \` afterwards to install a package and + save it as a dependency in the package.json file. + + Press ^C at any time to quit. + ), + ], +] ` exports[`test/lib/init.js TAP workspaces no args, existing folder > should print helper info 1`] = ` - +Array [] ` exports[`test/lib/init.js TAP workspaces with arg but missing workspace folder > should print helper info 1`] = ` - +Array [] ` diff --git a/test/fixtures/mock-npm.js b/test/fixtures/mock-npm.js index aa8d44020ee36..c972c35b31861 100644 --- a/test/fixtures/mock-npm.js +++ b/test/fixtures/mock-npm.js @@ -4,35 +4,54 @@ const realConfig = require('../../lib/utils/config') -const mockLog = { - clearProgress: () => {}, - disableProgress: () => {}, - enableProgress: () => {}, - http: () => {}, - info: () => {}, - levels: [], - notice: () => {}, - pause: () => {}, - silly: () => {}, - verbose: () => {}, - warn: () => {}, -} -const mockNpm = (base = {}) => { - const config = base.config || {} - const flatOptions = base.flatOptions || {} - return { - log: mockLog, - ...base, - flatOptions, - config: { +class MockNpm { + constructor (base = {}) { + this._mockOutputs = [] + this.isMockNpm = true + this.base = base + + const config = base.config || {} + + for (const attr in base) { + if (attr !== 'config') { + this[attr] = base[attr] + } + } + + this.flatOptions = base.flatOptions || {} + this.config = { // for now just set `find` to what config.find should return // this works cause `find` is not an existing config entry find: (k) => ({...realConfig.defaults, ...config})[k], get: (k) => ({...realConfig.defaults, ...config})[k], set: (k, v) => config[k] = v, list: [{ ...realConfig.defaults, ...config}] - }, + } + if (!this.log) { + this.log = { + clearProgress: () => {}, + disableProgress: () => {}, + enableProgress: () => {}, + http: () => {}, + info: () => {}, + levels: [], + notice: () => {}, + pause: () => {}, + silly: () => {}, + verbose: () => {}, + warn: () => {}, + } + } + } + + output(...msg) { + if (this.base.output) + return this.base.output(msg) + this._mockOutputs.push(msg) } } -module.exports = mockNpm +// TODO export MockNpm, and change tests to use new MockNpm() +module.exports = (base = {}) => { + return new MockNpm(base) +} diff --git a/test/lib/diff.js b/test/lib/diff.js index 355095c95786e..7a52ea5ee0ae1 100644 --- a/test/lib/diff.js +++ b/test/lib/diff.js @@ -4,7 +4,7 @@ const mockNpm = require('../fixtures/mock-npm') const noop = () => null let libnpmdiff = noop -let rlp = () => 'foo' +let rpn = () => 'foo' const config = { global: false, @@ -33,7 +33,7 @@ const mocks = { npmlog: { info: noop, verbose: noop }, libnpmdiff: (...args) => libnpmdiff(...args), 'npm-registry-fetch': async () => ({}), - '../../lib/utils/read-local-package.js': async () => rlp(), + '../../lib/utils/read-package-name.js': async (prefix) => rpn(prefix), '../../lib/utils/usage.js': () => 'usage instructions', } @@ -52,7 +52,7 @@ t.afterEach(() => { npm.globalDir = __dirname npm.prefix = '..' libnpmdiff = noop - rlp = () => 'foo' + rpn = () => 'foo' }) const Diff = t.mock('../../lib/diff.js', mocks) @@ -77,7 +77,7 @@ t.test('no args', t => { }) t.test('no args, missing package.json name in cwd', t => { - rlp = () => undefined + rpn = () => undefined diff.exec([], err => { t.match( @@ -90,7 +90,7 @@ t.test('no args', t => { }) t.test('no args, missing package.json in cwd', t => { - rlp = () => { + rpn = () => { throw new Error('ERR') } @@ -109,14 +109,17 @@ t.test('no args', t => { t.test('single arg', t => { t.test('spec using cwd package name', t => { - t.plan(3) + t.plan(4) + rpn = (prefix) => { + t.equal(prefix, path, 'read-package-name gets proper prefix') + return 'foo' + } const path = t.testdir({}) libnpmdiff = async ([a, b], opts) => { t.equal(a, 'foo@1.0.0', 'should forward single spec') t.equal(b, `file:${path}`, 'should compare to cwd') t.match(opts, npm.flatOptions, 'should forward flat options') - t.end() } config.diff = ['foo@1.0.0'] @@ -124,12 +127,13 @@ t.test('single arg', t => { diff.exec([], err => { if (err) throw err + t.end() }) }) t.test('unknown spec, no package.json', t => { const path = t.testdir({}) - rlp = () => { + rpn = () => { throw new Error('ERR') } @@ -182,7 +186,7 @@ t.test('single arg', t => { }) t.test('version, no package.json', t => { - rlp = () => { + rpn = () => { throw new Error('ERR') } @@ -273,7 +277,7 @@ t.test('single arg', t => { t.test('unknown package name, no package.json', t => { const path = t.testdir({}) - rlp = () => { + rpn = () => { throw new Error('ERR') } @@ -465,7 +469,7 @@ t.test('single arg', t => { const Diff = t.mock('../../lib/diff.js', { ...mocks, - '../../lib/utils/read-local-package.js': async () => 'my-project', + '../../lib/utils/read-package-name.js': async () => 'my-project', pacote: { packument: (spec) => { t.equal(spec.name, 'lorem', 'should have expected spec name') @@ -502,7 +506,7 @@ t.test('single arg', t => { const Diff = t.mock('../../lib/diff.js', { ...mocks, - '../../lib/utils/read-local-package.js': async () => 'my-project', + '../../lib/utils/read-package-name.js': async () => 'my-project', '@npmcli/arborist': class { constructor () { throw new Error('ERR') @@ -528,7 +532,7 @@ t.test('single arg', t => { t.plan(2) const path = t.testdir({}) - rlp = async () => undefined + rpn = async () => undefined libnpmdiff = async ([a, b], opts) => { t.equal(a, 'bar@latest', 'should target latest tag of name') t.equal(b, `file:${path}`, 'should compare to cwd') @@ -547,7 +551,7 @@ t.test('single arg', t => { t.plan(2) const path = t.testdir({}) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, 'my-project@latest', 'should target latest tag of name') t.equal(b, `file:${path}`, 'should compare to cwd') @@ -565,7 +569,7 @@ t.test('single arg', t => { t.plan(2) const path = t.testdir({}) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, 'file:/path/to/other-dir', 'should target dir') t.equal(b, `file:${path}`, 'should compare to cwd') @@ -580,7 +584,7 @@ t.test('single arg', t => { }) t.test('unsupported spec type', t => { - rlp = async () => 'my-project' + rpn = async () => 'my-project' config.diff = ['git+https://github.com/user/foo'] @@ -634,7 +638,7 @@ t.test('first arg is a qualified spec', t => { }), }) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, 'bar@2.0.0', 'should set expected first spec') t.equal(b, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') @@ -703,7 +707,7 @@ t.test('first arg is a known dependency name', t => { }), }) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') t.equal(b, 'bar@2.0.0', 'should set expected second spec') @@ -743,7 +747,7 @@ t.test('first arg is a known dependency name', t => { }), }) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') t.equal(b, `bar-fork@file:${resolve(path, 'node_modules/bar-fork')}`, 'should target fork local node_modules pkg') @@ -777,7 +781,7 @@ t.test('first arg is a known dependency name', t => { }), }) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') t.equal(b, 'bar@2.0.0', 'should use package name from first arg') @@ -811,7 +815,7 @@ t.test('first arg is a known dependency name', t => { }), }) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') t.equal(b, 'bar-fork@latest', 'should set expected second spec') @@ -865,7 +869,7 @@ t.test('first arg is a valid semver range', t => { }), }) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, 'bar@1.0.0', 'should use name from second arg') t.equal(b, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should set expected second spec from nm') @@ -882,7 +886,7 @@ t.test('first arg is a valid semver range', t => { t.test('second arg is ALSO a semver version', t => { t.plan(2) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, 'my-project@1.0.0', 'should use name from project dir') t.equal(b, 'my-project@2.0.0', 'should use name from project dir') @@ -897,7 +901,7 @@ t.test('first arg is a valid semver range', t => { t.test('second arg is ALSO a semver version BUT cwd not a project dir', t => { const path = t.testdir({}) - rlp = () => { + rpn = () => { throw new Error('ERR') } @@ -916,7 +920,7 @@ t.test('first arg is a valid semver range', t => { t.test('second arg is an unknown dependency name', t => { t.plan(2) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, 'bar@1.0.0', 'should use name from second arg') t.equal(b, 'bar@latest', 'should compare against latest tag') @@ -940,7 +944,7 @@ t.test('first arg is a valid semver range', t => { const Diff = t.mock('../../lib/diff.js', { ...mocks, - '../../lib/utils/read-local-package.js': async () => 'my-project', + '../../lib/utils/read-package-name.js': async () => 'my-project', '@npmcli/arborist': class { constructor () { throw new Error('ERR') @@ -1003,7 +1007,7 @@ t.test('first arg is an unknown dependency name', t => { }), }) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, 'bar-fork@latest', 'should use latest tag') t.equal(b, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') @@ -1051,7 +1055,7 @@ t.test('first arg is an unknown dependency name', t => { t.plan(2) const path = t.testdir({}) - rlp = () => { + rpn = () => { throw new Error('ERR') } libnpmdiff = async ([a, b], opts) => { @@ -1117,7 +1121,7 @@ t.test('various options', t => { t.plan(3) const path = t.testdir({}) - rlp = async () => 'my-project' + rpn = async () => 'my-project' libnpmdiff = async ([a, b], opts) => { t.equal(a, 'my-project@latest', 'should have default spec') t.equal(b, `file:${path}`, 'should compare to cwd') diff --git a/test/lib/dist-tag.js b/test/lib/dist-tag.js index 6bc17168cdce0..9af90c309c77c 100644 --- a/test/lib/dist-tag.js +++ b/test/lib/dist-tag.js @@ -93,6 +93,20 @@ t.test('ls in current package', (t) => { }) }) +t.test('ls global', (t) => { + t.teardown(() => { + config.global = false + }) + config.global = true + distTag.exec(['ls'], (err) => { + t.matchSnapshot( + err, + 'should throw basic usage' + ) + t.end() + }) +}) + t.test('no args in current package', (t) => { npm.prefix = t.testdir({ 'package.json': JSON.stringify({ diff --git a/test/lib/exec.js b/test/lib/exec.js index 33e30e24f84e0..6924783239b49 100644 --- a/test/lib/exec.js +++ b/test/lib/exec.js @@ -1,8 +1,6 @@ const t = require('tap') const mockNpm = require('../fixtures/mock-npm') const { resolve, delimiter } = require('path') -const OUTPUT = [] -const output = (...msg) => OUTPUT.push(msg) const ARB_CTOR = [] const ARB_ACTUAL_TREE = {} @@ -36,6 +34,7 @@ const config = { package: [], 'script-shell': 'shell-cmd', } + const npm = mockNpm({ flatOptions, config, @@ -53,7 +52,6 @@ const npm = mockNpm({ LOG_WARN.push(args) }, }, - output, }) const RUN_SCRIPTS = [] @@ -225,7 +223,7 @@ t.test('npm exec , run interactive shell', t => { ARB_CTOR.length = 0 MKDIRPS.length = 0 ARB_REIFY.length = 0 - OUTPUT.length = 0 + npm._mockOutputs.length = 0 exec.exec([], er => { if (er) throw er @@ -256,7 +254,7 @@ t.test('npm exec , run interactive shell', t => { process.stdin.isTTY = true run(t, true, () => { t.strictSame(LOG_WARN, []) - t.strictSame(OUTPUT, [ + t.strictSame(npm._mockOutputs, [ [`\nEntering npm script environment at location:\n${process.cwd()}\nType 'exit' or ^D when finished\n`], ], 'printed message about interactive shell') t.end() @@ -270,7 +268,7 @@ t.test('npm exec , run interactive shell', t => { run(t, true, () => { t.strictSame(LOG_WARN, []) - t.strictSame(OUTPUT, [ + t.strictSame(npm._mockOutputs, [ [`\u001b[0m\u001b[0m\n\u001b[0mEntering npm script environment\u001b[0m\u001b[0m at location:\u001b[0m\n\u001b[0m\u001b[2m${process.cwd()}\u001b[22m\u001b[0m\u001b[1m\u001b[22m\n\u001b[1mType 'exit' or ^D when finished\u001b[22m\n\u001b[1m\u001b[22m`], ], 'printed message about interactive shell') t.end() @@ -282,7 +280,7 @@ t.test('npm exec , run interactive shell', t => { process.stdin.isTTY = false run(t, true, () => { t.strictSame(LOG_WARN, []) - t.strictSame(OUTPUT, [], 'no message about interactive shell') + t.strictSame(npm._mockOutputs, [], 'no message about interactive shell') t.end() }) }) @@ -294,7 +292,7 @@ t.test('npm exec , run interactive shell', t => { t.strictSame(LOG_WARN, [ ['exec', 'Interactive mode disabled in CI environment'], ]) - t.strictSame(OUTPUT, [], 'no message about interactive shell') + t.strictSame(npm._mockOutputs, [], 'no message about interactive shell') t.end() }) }) @@ -309,14 +307,14 @@ t.test('npm exec , run interactive shell', t => { throw er t.match(RUN_SCRIPTS, [{ - pkg: { scripts: { npx: undefined } }, + pkg: { scripts: { npx: /sh|cmd/ } }, }]) LOG_WARN.length = 0 ARB_CTOR.length = 0 MKDIRPS.length = 0 ARB_REIFY.length = 0 - OUTPUT.length = 0 + npm._mockOutputs.length = 0 RUN_SCRIPTS.length = 0 t.end() }) @@ -1195,7 +1193,7 @@ t.test('workspaces', t => { return rej(er) t.strictSame(LOG_WARN, []) - t.strictSame(OUTPUT, [ + t.strictSame(npm._mockOutputs, [ [`\nEntering npm script environment in workspace a@1.0.0 at location:\n${resolve(npm.localPrefix, 'packages/a')}\nType 'exit' or ^D when finished\n`], ], 'printed message about interactive shell') res() @@ -1203,14 +1201,14 @@ t.test('workspaces', t => { }) config.color = true - OUTPUT.length = 0 + npm._mockOutputs.length = 0 await new Promise((res, rej) => { exec.execWorkspaces([], ['a'], er => { if (er) return rej(er) t.strictSame(LOG_WARN, []) - t.strictSame(OUTPUT, [ + t.strictSame(npm._mockOutputs, [ [`\u001b[0m\u001b[0m\n\u001b[0mEntering npm script environment\u001b[0m\u001b[0m in workspace \u001b[32ma@1.0.0\u001b[39m at location:\u001b[0m\n\u001b[0m\u001b[2m${resolve(npm.localPrefix, 'packages/a')}\u001b[22m\u001b[0m\u001b[1m\u001b[22m\n\u001b[1mType 'exit' or ^D when finished\u001b[22m\n\u001b[1m\u001b[22m`], ], 'printed message about interactive shell') res() diff --git a/test/lib/init.js b/test/lib/init.js index 0964bb5cedde6..268b170cb4839 100644 --- a/test/lib/init.js +++ b/test/lib/init.js @@ -3,7 +3,6 @@ const { resolve } = require('path') const t = require('tap') const mockNpm = require('../fixtures/mock-npm') -let result = '' const npmLog = { disableProgress: () => null, enableProgress: () => null, @@ -19,9 +18,6 @@ const config = { const npm = mockNpm({ config, log: npmLog, - output: (...msg) => { - result += msg.join('\n') - }, }) const mocks = { '../../lib/utils/usage.js': () => 'usage instructions', @@ -33,7 +29,6 @@ const _consolelog = console.log const noop = () => {} t.afterEach(() => { - result = '' config.yes = true config.package = undefined npm.log = npmLog @@ -322,6 +317,9 @@ t.test('npm init error', t => { t.test('workspaces', t => { t.test('no args', t => { + t.teardown(() => { + npm._mockOutputs.length = 0 + }) npm.localPrefix = t.testdir({ 'package.json': JSON.stringify({ name: 'top-level', @@ -340,12 +338,15 @@ t.test('workspaces', t => { if (err) throw err - t.matchSnapshot(result, 'should print helper info') + t.matchSnapshot(npm._mockOutputs, 'should print helper info') t.end() }) }) t.test('no args, existing folder', t => { + t.teardown(() => { + npm._mockOutputs.length = 0 + }) // init-package-json prints directly to console.log // this avoids poluting test output with those logs console.log = noop @@ -369,12 +370,15 @@ t.test('workspaces', t => { if (err) throw err - t.matchSnapshot(result, 'should print helper info') + t.matchSnapshot(npm._mockOutputs, 'should print helper info') t.end() }) }) t.test('with arg but missing workspace folder', t => { + t.teardown(() => { + npm._mockOutputs.length = 0 + }) // init-package-json prints directly to console.log // this avoids poluting test output with those logs console.log = noop @@ -401,7 +405,7 @@ t.test('workspaces', t => { if (err) throw err - t.matchSnapshot(result, 'should print helper info') + t.matchSnapshot(npm._mockOutputs, 'should print helper info') t.end() }) }) diff --git a/test/lib/owner.js b/test/lib/owner.js index 4af8d1ebbb8fa..10ceb03030a5a 100644 --- a/test/lib/owner.js +++ b/test/lib/owner.js @@ -1,16 +1,18 @@ const t = require('tap') +const mockNpm = require('../fixtures/mock-npm.js') let result = '' -let readLocalPkgResponse = null +let readPackageNamePrefix = null +let readPackageNameResponse = null const noop = () => null -const npm = { - flatOptions: {}, +const npm = mockNpm({ output: (msg) => { result = result ? `${result}\n${msg}` : msg }, -} +}) + const npmFetch = { json: noop } const npmlog = { error: noop, info: noop, verbose: noop } const pacote = { packument: noop } @@ -20,7 +22,10 @@ const mocks = { 'npm-registry-fetch': npmFetch, pacote, '../../lib/utils/otplease.js': async (opts, fn) => fn({ otp: '123456', opts }), - '../../lib/utils/read-local-package.js': async () => readLocalPkgResponse, + '../../lib/utils/read-package-name.js': async (prefix) => { + readPackageNamePrefix = prefix + return readPackageNameResponse + }, '../../lib/utils/usage.js': () => 'usage instructions', } @@ -47,11 +52,11 @@ t.test('owner no args', t => { }) t.test('owner ls no args', t => { - t.plan(4) + t.plan(5) result = '' - readLocalPkgResponse = '@npmcli/map-workspaces' + readPackageNameResponse = '@npmcli/map-workspaces' pacote.packument = async (spec, opts) => { t.equal(spec.name, '@npmcli/map-workspaces', 'should use expect pkg name') t.match( @@ -65,14 +70,29 @@ t.test('owner ls no args', t => { return { maintainers: npmcliMaintainers } } t.teardown(() => { + npm.prefix = null result = '' pacote.packument = noop - readLocalPkgResponse = null + readPackageNameResponse = null }) + npm.prefix = 'test-npm-prefix' owner.exec(['ls'], err => { t.error(err, 'npm owner ls no args') t.matchSnapshot(result, 'should output owners of cwd package') + t.equal(readPackageNamePrefix, 'test-npm-prefix', 'read-package-name gets npm.prefix') + }) +}) + +t.test('owner ls global', t => { + t.teardown(() => { + npm.config.set('global', false) + }) + npm.config.set('global', true) + + owner.exec(['ls'], err => { + t.match(err, /usage instructions/, 'should throw usage instructions if no cwd package available') + t.end() }) }) @@ -93,7 +113,7 @@ t.test('owner ls fails to retrieve packument', t => { t.plan(4) result = '' - readLocalPkgResponse = '@npmcli/map-workspaces' + readPackageNameResponse = '@npmcli/map-workspaces' pacote.packument = () => { throw new Error('ERR') } @@ -233,7 +253,7 @@ t.test('owner add ', t => { t.test('owner add cwd package', t => { result = '' - readLocalPkgResponse = '@npmcli/map-workspaces' + readPackageNameResponse = '@npmcli/map-workspaces' npmFetch.json = async (uri, opts) => { // retrieve user info from couchdb request if (uri === '/-/user/org.couchdb.user:foo') { @@ -253,7 +273,7 @@ t.test('owner add cwd package', t => { }) t.teardown(() => { result = '' - readLocalPkgResponse = null + readPackageNameResponse = null npmFetch.json = noop pacote.packument = noop }) @@ -308,7 +328,7 @@ t.test('owner add already an owner', t => { t.test('owner add fails to retrieve user', t => { result = '' - readLocalPkgResponse = + readPackageNameResponse = npmFetch.json = async (uri, opts) => { // retrieve borked user info from couchdb request if (uri === '/-/user/org.couchdb.user:foo') @@ -324,7 +344,7 @@ t.test('owner add fails to retrieve user', t => { }) t.teardown(() => { result = '' - readLocalPkgResponse = null + readPackageNameResponse = null npmFetch.json = noop pacote.packument = noop }) @@ -465,6 +485,18 @@ t.test('owner add no user', t => { }) }) +t.test('owner add no pkg global', t => { + t.teardown(() => { + npm.config.set('global', false) + }) + npm.config.set('global', true) + + owner.exec(['add', 'gar'], err => { + t.match(err, /usage instructions/, 'should throw usage instructions if user provided') + t.end() + }) +}) + t.test('owner add no cwd package', t => { result = '' t.teardown(() => { @@ -581,7 +613,7 @@ t.test('owner rm not a current owner', t => { t.test('owner rm cwd package', t => { result = '' - readLocalPkgResponse = '@npmcli/map-workspaces' + readPackageNameResponse = '@npmcli/map-workspaces' npmFetch.json = async (uri, opts) => { // retrieve user info from couchdb request if (uri === '/-/user/org.couchdb.user:ruyadorno') { @@ -601,7 +633,7 @@ t.test('owner rm cwd package', t => { }) t.teardown(() => { result = '' - readLocalPkgResponse = null + readPackageNameResponse = null npmFetch.json = noop pacote.packument = noop }) @@ -615,7 +647,7 @@ t.test('owner rm cwd package', t => { t.test('owner rm only user', t => { result = '' - readLocalPkgResponse = 'ipt' + readPackageNameResponse = 'ipt' npmFetch.json = async (uri, opts) => { // retrieve user info from couchdb request if (uri === '/-/user/org.couchdb.user:ruyadorno') { @@ -636,7 +668,7 @@ t.test('owner rm only user', t => { }) t.teardown(() => { result = '' - readLocalPkgResponse = null + readPackageNameResponse = null npmFetch.json = noop pacote.packument = noop }) @@ -664,6 +696,18 @@ t.test('owner rm no user', t => { }) }) +t.test('owner rm no pkg global', t => { + t.teardown(() => { + npm.config.set('global', false) + }) + npm.config.set('global', true) + + owner.exec(['rm', 'gar'], err => { + t.match(err, /usage instructions/, 'should throw usage instructions if user provided') + t.end() + }) +}) + t.test('owner rm no cwd package', t => { result = '' t.teardown(() => { @@ -693,15 +737,15 @@ t.test('completion', async t => { // npm owner rm completion is async t.test('completion npm owner rm', async t => { t.plan(2) - readLocalPkgResponse = '@npmcli/map-workspaces' + readPackageNameResponse = '@npmcli/map-workspaces' pacote.packument = async spec => { - t.equal(spec.name, readLocalPkgResponse, 'should use package spec') + t.equal(spec.name, readPackageNameResponse, 'should use package spec') return { maintainers: npmcliMaintainers, } } t.teardown(() => { - readLocalPkgResponse = null + readPackageNameResponse = null pacote.packument = noop }) @@ -718,17 +762,27 @@ t.test('completion', async t => { t.end() }) + t.test('completion npm owner rm global', async t => { + t.teardown(() => { + npm.config.set('global', false) + }) + npm.config.set('global', true) + const res = await owner.completion({ conf: { argv: { remain: ['npm', 'owner', 'rm'] } } }) + t.strictSame(res, [], 'should have no owners to autocomplete if global') + t.end() + }) + t.test('completion npm owner rm no owners found', async t => { t.plan(2) - readLocalPkgResponse = '@npmcli/map-workspaces' + readPackageNameResponse = '@npmcli/map-workspaces' pacote.packument = async spec => { - t.equal(spec.name, readLocalPkgResponse, 'should use package spec') + t.equal(spec.name, readPackageNameResponse, 'should use package spec') return { maintainers: [], } } t.teardown(() => { - readLocalPkgResponse = null + readPackageNameResponse = null pacote.packument = noop }) diff --git a/test/lib/utils/read-local-package.js b/test/lib/utils/read-package-name.js similarity index 55% rename from test/lib/utils/read-local-package.js rename to test/lib/utils/read-package-name.js index 966e74a7ab7f4..c8f88bacd4b84 100644 --- a/test/lib/utils/read-local-package.js +++ b/test/lib/utils/read-package-name.js @@ -1,13 +1,8 @@ const t = require('tap') const mockNpm = require('../../fixtures/mock-npm') +const npm = mockNpm() -const config = { - json: false, - global: false, -} -const npm = mockNpm({ config }) - -const readLocalPackageName = require('../../../lib/utils/read-local-package.js') +const readPackageName = require('../../../lib/utils/read-package-name.js') t.test('read local package.json', async (t) => { npm.prefix = t.testdir({ @@ -16,7 +11,7 @@ t.test('read local package.json', async (t) => { version: '1.0.0', }), }) - const packageName = await readLocalPackageName(npm) + const packageName = await readPackageName(npm.prefix) t.equal( packageName, 'my-local-package', @@ -31,22 +26,10 @@ t.test('read local scoped-package.json', async (t) => { version: '1.0.0', }), }) - const packageName = await readLocalPackageName(npm) + const packageName = await readPackageName(npm.prefix) t.equal( packageName, '@my-scope/my-local-package', 'should retrieve scoped package name' ) }) - -t.test('read using --global', async (t) => { - npm.prefix = t.testdir({}) - config.global = true - const packageName = await readLocalPackageName(npm) - t.equal( - packageName, - undefined, - 'should not retrieve a package name' - ) - config.global = false -})