diff options
author | Ruy Adorno <ruyadorno@hotmail.com> | 2021-03-23 14:58:11 -0400 |
---|---|---|
committer | Ruy Adorno <ruyadorno@hotmail.com> | 2021-03-23 22:10:30 -0400 |
commit | 73b3e06c910549a7fd3c8f49324ab14e0adf2c8d (patch) | |
tree | e7f0ab70d8bb09a6212f8a22da25dc07410e6967 /deps/npm/test/lib | |
parent | f2090877f1f727020099ef9e30cc70e10b10f900 (diff) | |
download | node-new-73b3e06c910549a7fd3c8f49324ab14e0adf2c8d.tar.gz |
deps: upgrade npm to 7.7.0
PR-URL: https://github.com/nodejs/node/pull/37879
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Diffstat (limited to 'deps/npm/test/lib')
52 files changed, 3170 insertions, 1868 deletions
diff --git a/deps/npm/test/lib/audit.js b/deps/npm/test/lib/audit.js index d291ef8794..a25e6b0e27 100644 --- a/deps/npm/test/lib/audit.js +++ b/deps/npm/test/lib/audit.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') t.test('should audit using Arborist', t => { let ARB_ARGS = null @@ -9,15 +10,15 @@ t.test('should audit using Arborist', t => { let OUTPUT_CALLED = false let ARB_OBJ = null - const npm = { + const npm = mockNpm({ prefix: 'foo', - flatOptions: { + config: { json: false, }, output: () => { OUTPUT_CALLED = true }, - } + }) const Audit = requireInject('../../lib/audit.js', { 'npm-audit-report': () => { AUDIT_REPORT_CALLED = true @@ -65,13 +66,13 @@ t.test('should audit using Arborist', t => { }) t.test('should audit - json', t => { - const npm = { + const npm = mockNpm({ prefix: 'foo', - flatOptions: { + config: { json: true, }, output: () => {}, - } + }) const Audit = requireInject('../../lib/audit.js', { 'npm-audit-report': () => ({ @@ -98,9 +99,12 @@ t.test('report endpoint error', t => { t.test(`json=${json}`, t => { const OUTPUT = [] const LOGS = [] - const npm = { + const npm = mockNpm({ prefix: 'foo', command: 'audit', + config: { + json, + }, flatOptions: { json, }, @@ -110,7 +114,7 @@ t.test('report endpoint error', t => { output: (...msg) => { OUTPUT.push(msg) }, - } + }) const Audit = requireInject('../../lib/audit.js', { 'npm-audit-report': () => { throw new Error('should not call audit report when there are errors') diff --git a/deps/npm/test/lib/bin.js b/deps/npm/test/lib/bin.js index 428b2e3bad..1d9341169b 100644 --- a/deps/npm/test/lib/bin.js +++ b/deps/npm/test/lib/bin.js @@ -1,5 +1,6 @@ const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('bin', (t) => { t.plan(4) @@ -7,13 +8,13 @@ test('bin', (t) => { const Bin = require('../../lib/bin.js') - const npm = { + const npm = mockNpm({ bin: dir, - flatOptions: { global: false }, + config: { global: false }, output: (output) => { t.equal(output, dir, 'prints the correct directory') }, - } + }) const bin = new Bin(npm) t.match(bin.usage, 'bin', 'usage has command name in it') @@ -39,13 +40,13 @@ test('bin -g', (t) => { '../../lib/utils/path.js': [dir], }) - const npm = { + const npm = mockNpm({ bin: dir, - flatOptions: { global: true }, + config: { global: true }, output: (output) => { t.equal(output, dir, 'prints the correct directory') }, - } + }) const bin = new Bin(npm) bin.exec([], (err) => { @@ -69,13 +70,13 @@ test('bin -g (not in path)', (t) => { const Bin = requireInject('../../lib/bin.js', { '../../lib/utils/path.js': ['/not/my/dir'], }) - const npm = { + const npm = mockNpm({ bin: dir, - flatOptions: { global: true }, + config: { global: true }, output: (output) => { t.equal(output, dir, 'prints the correct directory') }, - } + }) const bin = new Bin(npm) bin.exec([], (err) => { diff --git a/deps/npm/test/lib/birthday.js b/deps/npm/test/lib/birthday.js index c818223fb5..0589be7a8e 100644 --- a/deps/npm/test/lib/birthday.js +++ b/deps/npm/test/lib/birthday.js @@ -1,20 +1,23 @@ const t = require('tap') -const npm = { - flatOptions: { - yes: false, - package: [], - }, +const mockNpm = require('../fixtures/mock-npm') + +const config = { + yes: false, + package: [], +} +const npm = mockNpm({ + config, commands: { exec: (args, cb) => { - t.equal(npm.flatOptions.yes, true, 'should say yes') - t.strictSame(npm.flatOptions.package, ['@npmcli/npm-birthday'], + t.equal(npm.config.get('yes'), true, 'should say yes') + t.strictSame(npm.config.get('package'), ['@npmcli/npm-birthday'], 'uses correct package') t.strictSame(args, ['npm-birthday'], 'called with correct args') t.match(cb, Function, 'callback is a function') cb() }, }, -} +}) const Birthday = require('../../lib/birthday.js') const birthday = new Birthday(npm) diff --git a/deps/npm/test/lib/cache.js b/deps/npm/test/lib/cache.js index 773adc6a8a..0fdf768568 100644 --- a/deps/npm/test/lib/cache.js +++ b/deps/npm/test/lib/cache.js @@ -1,23 +1,12 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const path = require('path') const usageUtil = () => 'usage instructions' -const flatOptions = { - force: false, -} - let outputOutput = [] -const npm = { - flatOptions, - cache: '/fake/path', - output: (msg) => { - outputOutput.push(msg) - }, -} - let rimrafPath = '' const rimraf = (path, cb) => { rimrafPath = path @@ -66,6 +55,14 @@ const Cache = requireInject('../../lib/cache.js', { '../../lib/utils/usage.js': usageUtil, }) +const npm = mockNpm({ + cache: '/fake/path', + flatOptions: { force: false }, + config: { force: false }, + output: (msg) => { + outputOutput.push(msg) + }, +}) const cache = new Cache(npm) t.test('cache no args', t => { @@ -83,10 +80,12 @@ t.test('cache clean', t => { }) t.test('cache clean (force)', t => { - flatOptions.force = true + npm.config.set('force', true) + npm.flatOptions.force = true t.teardown(() => { rimrafPath = '' - flatOptions.force = false + npm.config.force = false + npm.flatOptions.force = false }) cache.exec(['clear'], err => { @@ -131,7 +130,7 @@ t.test('cache add pkg only', t => { ['silly', 'cache add', 'spec', 'mypkg'], ], 'logs correctly') t.equal(tarballStreamSpec, 'mypkg', 'passes the correct spec to pacote') - t.same(tarballStreamOpts, flatOptions, 'passes the correct options to pacote') + t.same(tarballStreamOpts, npm.flatOptions, 'passes the correct options to pacote') t.end() }) }) @@ -150,7 +149,7 @@ t.test('cache add pkg w/ spec modifier', t => { ['silly', 'cache add', 'spec', 'mypkg@latest'], ], 'logs correctly') t.equal(tarballStreamSpec, 'mypkg@latest', 'passes the correct spec to pacote') - t.same(tarballStreamOpts, flatOptions, 'passes the correct options to pacote') + t.same(tarballStreamOpts, npm.flatOptions, 'passes the correct options to pacote') t.end() }) }) diff --git a/deps/npm/test/lib/ci.js b/deps/npm/test/lib/ci.js index 3419218ef9..7f06a6cebc 100644 --- a/deps/npm/test/lib/ci.js +++ b/deps/npm/test/lib/ci.js @@ -5,6 +5,7 @@ const readdir = util.promisify(fs.readdir) const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('should ignore scripts with --ignore-scripts', (t) => { const SCRIPTS = [] @@ -22,17 +23,15 @@ test('should ignore scripts with --ignore-scripts', (t) => { }, }) - const ci = new CI({ + const npm = mockNpm({ globalDir: 'path/to/node_modules/', prefix: 'foo', - flatOptions: { - global: false, - ignoreScripts: true, - }, config: { - get: () => false, + global: false, + 'ignore-scripts': true, }, }) + const ci = new CI(npm) ci.exec([], er => { if (er) @@ -115,12 +114,13 @@ test('should use Arborist and run-script', (t) => { }, }) - const ci = new CI({ + const npm = mockNpm({ prefix: path, - flatOptions: { + config: { global: false, }, }) + const ci = new CI(npm) ci.exec(null, er => { if (er) @@ -146,12 +146,13 @@ test('should pass flatOptions to Arborist.reify', (t) => { } }, }) - const ci = new CI({ + const npm = mockNpm({ prefix: 'foo', flatOptions: { production: true, }, }) + const ci = new CI(npm) ci.exec(null, er => { if (er) throw er @@ -173,14 +174,15 @@ test('should throw if package-lock.json or npm-shrinkwrap missing', (t) => { }, }, }) - const ci = new CI({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { + config: { global: false, }, }) + const ci = new CI(npm) ci.exec(null, (err, res) => { - t.ok(err, 'throws error when there is no package-lock') + t.match(err, /package-lock.json/, 'throws error when there is no package-lock') t.notOk(res) t.end() }) @@ -191,12 +193,13 @@ test('should throw ECIGLOBAL', (t) => { '@npmcli/run-script': opts => {}, '../../lib/utils/reify-finish.js': async () => {}, }) - const ci = new CI({ + const npm = mockNpm({ prefix: 'foo', - flatOptions: { + config: { global: true, }, }) + const ci = new CI(npm) ci.exec(null, (err, res) => { t.equals(err.code, 'ECIGLOBAL', 'throws error with global packages') t.notOk(res) @@ -227,12 +230,13 @@ test('should remove existing node_modules before installing', (t) => { }, }) - const ci = new CI({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { + config: { global: false, }, }) + const ci = new CI(npm) ci.exec(null, er => { if (er) diff --git a/deps/npm/test/lib/cli.js b/deps/npm/test/lib/cli.js index b5441be1e4..40da77bf44 100644 --- a/deps/npm/test/lib/cli.js +++ b/deps/npm/test/lib/cli.js @@ -1,7 +1,11 @@ const t = require('tap') let LOAD_ERROR = null +const npmOutputs = [] const npmock = { + log: { level: 'silent' }, + output: (...msg) => npmOutputs.push(msg), + usage: 'npm usage test example', version: '99.99.99', load: cb => cb(LOAD_ERROR), argv: [], @@ -21,8 +25,11 @@ const unsupportedMock = { } let errorHandlerCalled = null +let errorHandlerCb const errorHandlerMock = (...args) => { errorHandlerCalled = args + if (errorHandlerCb) + errorHandlerCb() } let errorHandlerExitCalled = null errorHandlerMock.exit = code => { @@ -39,15 +46,23 @@ const npmlogMock = { const requireInject = require('require-inject') const cli = requireInject.installGlobally('../../lib/cli.js', { '../../lib/npm.js': npmock, + '../../lib/utils/did-you-mean.js': () => '\ntest did you mean', '../../lib/utils/unsupported.js': unsupportedMock, '../../lib/utils/error-handler.js': errorHandlerMock, npmlog: npmlogMock, }) t.test('print the version, and treat npm_g to npm -g', t => { - const { log } = console - const consoleLogs = [] - console.log = (...msg) => consoleLogs.push(msg) + t.teardown(() => { + delete npmock.config.settings.version + process.argv = argv + npmock.argv.length = 0 + proc.argv.length = 0 + logs.length = 0 + npmOutputs.length = 0 + errorHandlerExitCalled = null + }) + const { argv } = process const proc = { argv: ['node', 'npm_g', '-v'], @@ -67,25 +82,13 @@ t.test('print the version, and treat npm_g to npm -g', t => { ['info', 'using', 'npm@%s', '99.99.99'], ['info', 'using', 'node@%s', '420.69.lol'], ]) - t.strictSame(consoleLogs, [['99.99.99']]) + t.strictSame(npmOutputs, [['99.99.99']]) t.strictSame(errorHandlerExitCalled, 0) - delete npmock.config.settings.version - process.argv = argv - console.log = log - npmock.argv.length = 0 - proc.argv.length = 0 - logs.length = 0 - consoleLogs.length = 0 - errorHandlerExitCalled = null - t.end() }) t.test('calling with --versions calls npm version with no args', t => { - const { log } = console - const consoleLogs = [] - console.log = (...msg) => consoleLogs.push(msg) const processArgv = process.argv const proc = { argv: ['node', 'npm', 'install', 'or', 'whatever', '--versions'], @@ -97,11 +100,10 @@ t.test('calling with --versions calls npm version with no args', t => { t.teardown(() => { delete npmock.config.settings.versions process.argv = processArgv - console.log = log npmock.argv.length = 0 proc.argv.length = 0 logs.length = 0 - consoleLogs.length = 0 + npmOutputs.length = 0 errorHandlerExitCalled = null delete npmock.commands.version }) @@ -117,7 +119,7 @@ t.test('calling with --versions calls npm version with no args', t => { ['info', 'using', 'node@%s', undefined], ]) - t.strictSame(consoleLogs, []) + t.strictSame(npmOutputs, []) t.strictSame(errorHandlerExitCalled, null) t.strictSame(args, []) @@ -127,55 +129,80 @@ t.test('calling with --versions calls npm version with no args', t => { cli(proc) }) -t.test('print usage if -h provided', t => { - const { log } = console - const consoleLogs = [] - console.log = (...msg) => consoleLogs.push(msg) +t.test('print usage if no params provided', t => { + const { output } = npmock + t.teardown(() => { + npmock.output = output + }) + const proc = { + argv: ['node', 'npm'], + on: () => {}, + } + npmock.argv = [] + npmock.output = (msg) => { + if (msg) { + t.match(msg, 'npm usage test example', 'outputs npm usage') + t.end() + } + } + cli(proc) +}) + +t.test('print usage if non-command param provided', t => { + const { output } = npmock + t.teardown(() => { + npmock.output = output + }) const proc = { argv: ['node', 'npm', 'asdf'], on: () => {}, } npmock.argv = ['asdf'] + npmock.output = (msg) => { + if (msg) { + t.match(msg, 'Unknown command: "asdf"\ntest did you mean', 'outputs did you mean') + t.end() + } + } + cli(proc) +}) +t.test('gracefully handles error printing usage', t => { + const { output } = npmock t.teardown(() => { - console.log = log - npmock.argv.length = 0 - proc.argv.length = 0 - logs.length = 0 - consoleLogs.length = 0 - errorHandlerExitCalled = null - delete npmock.commands.help + npmock.output = output + errorHandlerCb = null }) - - npmock.commands.help = (args, cb) => { - delete npmock.commands.help - t.equal(proc.title, 'npm') - t.strictSame(args, ['asdf']) - t.strictSame(npmock.argv, ['asdf']) - t.strictSame(proc.argv, ['node', 'npm', 'asdf']) - t.strictSame(logs, [ - 'pause', - ['verbose', 'cli', ['node', 'npm', 'asdf']], - ['info', 'using', 'npm@%s', '99.99.99'], - ['info', 'using', 'node@%s', undefined], - ]) - t.strictSame(consoleLogs, []) - t.strictSame(errorHandlerExitCalled, null) + const proc = { + argv: ['node', 'npm', 'asdf'], + on: () => {}, + } + npmock.argv = [] + npmock.output = (msg) => { + throw new Error('test exception') + } + errorHandlerCb = () => { + t.match(errorHandlerCalled, /test exception/) t.end() } - cli(proc) }) t.test('load error calls error handler', t => { - const er = new Error('poop') + t.teardown(() => { + errorHandlerCb = null + LOAD_ERROR = null + }) + + const er = new Error('test load error') LOAD_ERROR = er const proc = { argv: ['node', 'npm', 'asdf'], on: () => {}, } + errorHandlerCb = () => { + t.strictSame(errorHandlerCalled, [er]) + t.end() + } cli(proc) - t.strictSame(errorHandlerCalled, [er]) - LOAD_ERROR = null - t.end() }) diff --git a/deps/npm/test/lib/completion.js b/deps/npm/test/lib/completion.js index 708f138251..c6ef901a7e 100644 --- a/deps/npm/test/lib/completion.js +++ b/deps/npm/test/lib/completion.js @@ -63,11 +63,14 @@ const cmdList = { plumbing: [], } +// only include a subset so that the snapshots aren't huge and +// don't change when we add/remove config definitions. +const definitions = require('../../lib/utils/config/definitions.js') const config = { - types: { - global: Boolean, - browser: [null, Boolean, String], - registry: [null, String], + definitions: { + global: definitions.global, + browser: definitions.browser, + registry: definitions.registry, }, shorthands: { reg: ['--registry'], @@ -80,7 +83,7 @@ const deref = (cmd) => { const Completion = requireInject('../../lib/completion.js', { '../../lib/utils/cmd-list.js': cmdList, - '../../lib/utils/config.js': config, + '../../lib/utils/config/index.js': config, '../../lib/utils/deref-command.js': deref, '../../lib/utils/is-windows-shell.js': false, }) diff --git a/deps/npm/test/lib/config.js b/deps/npm/test/lib/config.js index 3aeb29f8d3..14cd816171 100644 --- a/deps/npm/test/lib/config.js +++ b/deps/npm/test/lib/config.js @@ -1,4 +1,5 @@ const t = require('tap') + const requireInject = require('require-inject') const { EventEmitter } = require('events') @@ -22,12 +23,21 @@ const redactCwd = (path) => { t.cleanSnapshot = (str) => redactCwd(str) let result = '' -const types = { - 'init-author-name': String, - 'init-version': String, - 'init.author.name': String, - 'init.version': String, -} + +const configDefs = require('../../lib/utils/config') +const definitions = Object.entries(configDefs.definitions) + .filter(([key, def]) => { + return [ + 'init-author-name', + 'init.author.name', + 'init-version', + 'init.version', + ].includes(key) + }).reduce((defs, [key, def]) => { + defs[key] = def + return defs + }, {}) + const defaults = { 'init-author-name': '', 'init-version': '1.0.0', @@ -35,7 +45,7 @@ const defaults = { 'init.version': '1.0.0', } -const flatOptions = { +const cliConfig = { editor: 'vi', json: false, long: false, @@ -43,7 +53,6 @@ const flatOptions = { } const npm = { - flatOptions, log: { info: () => null, enableProgress: () => null, @@ -53,10 +62,10 @@ const npm = { data: new Map(Object.entries({ default: { data: defaults, source: 'default values' }, global: { data: {}, source: '/etc/npmrc' }, - cli: { data: flatOptions, source: 'command line options' }, + cli: { data: cliConfig, source: 'command line options' }, })), get (key) { - return flatOptions[key] + return cliConfig[key] }, validate () { return true @@ -70,7 +79,7 @@ const npm = { const usageUtil = () => 'usage instructions' const mocks = { - '../../lib/utils/config.js': { defaults, types }, + '../../lib/utils/config/index.js': { defaults, definitions }, '../../lib/utils/usage.js': usageUtil, } @@ -110,13 +119,13 @@ t.test('config list overrides', t => { }, source: '~/.npmrc', }) - flatOptions['init.author.name'] = 'Bar' + cliConfig['init.author.name'] = 'Bar' npm.config.find = () => 'cli' result = '' t.teardown(() => { result = '' npm.config.data.delete('user') - delete flatOptions['init.author.name'] + delete cliConfig['init.author.name'] delete npm.config.find }) @@ -129,12 +138,12 @@ t.test('config list overrides', t => { t.test('config list --long', t => { t.plan(2) - npm.config.find = key => key in flatOptions ? 'cli' : 'default' - flatOptions.long = true + npm.config.find = key => key in cliConfig ? 'cli' : 'default' + cliConfig.long = true result = '' t.teardown(() => { delete npm.config.find - flatOptions.long = false + cliConfig.long = false result = '' }) @@ -147,7 +156,7 @@ t.test('config list --long', t => { t.test('config list --json', t => { t.plan(2) - flatOptions.json = true + cliConfig.json = true result = '' npm.config.list = [{ '//private-reg.npmjs.org/:_authThoken': 'f00ba1', @@ -158,7 +167,7 @@ t.test('config list --json', t => { t.teardown(() => { delete npm.config.list - flatOptions.json = false + cliConfig.json = false npm.config.get = npmConfigGet result = '' }) @@ -246,13 +255,13 @@ t.test('config delete key --global', t => { t.equal(where, 'global', 'should save global config post-delete') } - flatOptions.global = true + cliConfig.global = true config.exec(['delete', 'foo'], (err) => { t.ifError(err, 'npm config delete key --global') }) t.teardown(() => { - flatOptions.global = false + cliConfig.global = false delete npm.config.delete delete npm.config.save }) @@ -401,13 +410,13 @@ t.test('config set key --global', t => { t.equal(where, 'global', 'should save global config') } - flatOptions.global = true + cliConfig.global = true config.exec(['set', 'foo', 'bar'], (err) => { t.ifError(err, 'npm config set key --global') }) t.teardown(() => { - flatOptions.global = false + cliConfig.global = false delete npm.config.set delete npm.config.save }) @@ -555,7 +564,7 @@ sign-git-commit=true` t.test('config edit --global', t => { t.plan(6) - flatOptions.global = true + cliConfig.global = true const npmrc = 'init.author.name=Foo' npm.config.data.set('global', { source: '/etc/npmrc', @@ -595,7 +604,7 @@ t.test('config edit --global', t => { }) t.teardown(() => { - flatOptions.global = false + cliConfig.global = false npm.config.data.delete('user') delete npm.config.save }) @@ -612,7 +621,7 @@ t.test('completion', t => { testComp(['npm', 'config'], ['get', 'set', 'delete', 'ls', 'rm', 'edit', 'list']) testComp(['npm', 'config', 'set', 'foo'], []) - const possibleConfigKeys = [...Object.keys(types)] + const possibleConfigKeys = [...Object.keys(definitions)] testComp(['npm', 'config', 'get'], possibleConfigKeys) testComp(['npm', 'config', 'set'], possibleConfigKeys) testComp(['npm', 'config', 'delete'], possibleConfigKeys) diff --git a/deps/npm/test/lib/dedupe.js b/deps/npm/test/lib/dedupe.js index 3e8b2f4c01..851163f935 100644 --- a/deps/npm/test/lib/dedupe.js +++ b/deps/npm/test/lib/dedupe.js @@ -1,20 +1,13 @@ const { test } = require('tap') const requireInject = require('require-inject') - -const npm = (base) => { - const config = base.config - return { - ...base, - flatOptions: { dryRun: false }, - config: { - get: (k) => config[k], - }, - } -} +const mockNpm = require('../fixtures/mock-npm') test('should throw in global mode', (t) => { const Dedupe = requireInject('../../lib/dedupe.js') - const dedupe = new Dedupe(npm({ config: { global: true }})) + const npm = mockNpm({ + config: { 'dry-run': false, global: true }, + }) + const dedupe = new Dedupe(npm) dedupe.exec([], er => { t.match(er, { code: 'EDEDUPEGLOBAL' }, 'throws EDEDUPEGLOBAL') @@ -36,12 +29,13 @@ test('should remove dupes using Arborist', (t) => { t.ok(arb, 'gets arborist tree') }, }) - const dedupe = new Dedupe(npm({ + const npm = mockNpm({ prefix: 'foo', config: { 'dry-run': 'true', }, - })) + }) + const dedupe = new Dedupe(npm) dedupe.exec([], er => { if (er) throw er @@ -53,17 +47,18 @@ test('should remove dupes using Arborist', (t) => { test('should remove dupes using Arborist - no arguments', (t) => { const Dedupe = requireInject('../../lib/dedupe.js', { '@npmcli/arborist': function (args) { - t.ok(args.dryRun, 'gets dryRun from flatOptions') + t.ok(args.dryRun, 'gets dryRun from config') this.dedupe = () => {} }, '../../lib/utils/reify-output.js': () => {}, }) - const dedupe = new Dedupe(npm({ + const npm = mockNpm({ prefix: 'foo', config: { - 'dry-run': true, + 'dry-run': 'true', }, - })) + }) + const dedupe = new Dedupe(npm) dedupe.exec(null, () => { t.end() }) diff --git a/deps/npm/test/lib/diff.js b/deps/npm/test/lib/diff.js index 9f58505dca..08761c64c8 100644 --- a/deps/npm/test/lib/diff.js +++ b/deps/npm/test/lib/diff.js @@ -1,30 +1,35 @@ const { resolve } = require('path') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const noop = () => null let libnpmdiff = noop let rlp = () => 'foo' -const defaultFlatOptions = { - defaultTag: 'latest', + +const config = { + global: false, + tag: 'latest', diff: [], +} +const flatOptions = { + global: false, diffUnified: null, diffIgnoreAllSpace: false, diffNoPrefix: false, diffSrcPrefix: '', diffDstPrefix: '', diffText: false, - prefix: '.', savePrefix: '^', } -const npm = { +const npm = mockNpm({ globalDir: __dirname, - flatOptions: { ...defaultFlatOptions }, - get prefix () { - return this.flatOptions.prefix - }, + prefix: '.', + config, + flatOptions, output: noop, -} +}) + const mocks = { npmlog: { info: noop, verbose: noop }, libnpmdiff: (...args) => libnpmdiff(...args), @@ -34,10 +39,21 @@ const mocks = { } t.afterEach(cb => { - npm.flatOptions = { ...defaultFlatOptions } + config.global = false + config.tag = 'latest' + config.diff = [] + flatOptions.global = false + flatOptions.diffUnified = null + flatOptions.diffIgnoreAllSpace = false + flatOptions.diffNoPrefix = false + flatOptions.diffSrcPrefix = '' + flatOptions.diffDstPrefix = '' + flatOptions.diffText = false + flatOptions.savePrefix = '^' + npm.globalDir = __dirname + npm.prefix = '..' libnpmdiff = noop rlp = () => 'foo' - npm.globalDir = __dirname cb() }) @@ -55,7 +71,7 @@ t.test('no args', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.prefix = path + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -102,10 +118,11 @@ t.test('single arg', t => { 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() } - npm.flatOptions.diff = ['foo@1.0.0'] - npm.flatOptions.prefix = path + config.diff = ['foo@1.0.0'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -118,8 +135,8 @@ t.test('single arg', t => { throw new Error('ERR') } - npm.flatOptions.diff = ['foo@1.0.0'] - npm.flatOptions.prefix = path + config.diff = ['foo@1.0.0'] + npm.prefix = path diff.exec([], err => { t.match( err, @@ -140,8 +157,8 @@ t.test('single arg', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.diff = ['foo@~1.0.0'] - npm.flatOptions.prefix = path + config.diff = ['foo@~1.0.0'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -158,8 +175,8 @@ t.test('single arg', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.diff = ['2.1.4'] - npm.flatOptions.prefix = path + config.diff = ['2.1.4'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -171,7 +188,7 @@ t.test('single arg', t => { throw new Error('ERR') } - npm.flatOptions.diff = ['2.1.4'] + config.diff = ['2.1.4'] diff.exec([], err => { t.match( err, @@ -198,8 +215,8 @@ t.test('single arg', t => { }, 'should forward flatOptions and diffFiles') } - npm.flatOptions.diff = ['2.1.4'] - npm.flatOptions.prefix = path + config.diff = ['2.1.4'] + npm.prefix = path diff.exec(['./foo.js', './bar.js'], err => { if (err) throw err @@ -221,8 +238,8 @@ t.test('single arg', t => { t.equal(b, `file:${path}`, 'should compare to cwd') } - npm.flatOptions.diff = ['bar@1.0.0'] - npm.flatOptions.prefix = path + config.diff = ['bar@1.0.0'] + npm.prefix = path diff.exec([], err => { if (err) @@ -248,8 +265,8 @@ t.test('single arg', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.diff = ['simple-output'] - npm.flatOptions.prefix = path + config.diff = ['simple-output'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -262,8 +279,8 @@ t.test('single arg', t => { throw new Error('ERR') } - npm.flatOptions.diff = ['bar'] - npm.flatOptions.prefix = path + config.diff = ['bar'] + npm.prefix = path diff.exec([], err => { t.match( err, @@ -294,8 +311,8 @@ t.test('single arg', t => { }), }) - npm.flatOptions.diff = ['bar'] - npm.flatOptions.prefix = path + config.diff = ['bar'] + npm.prefix = path const Diff = requireInject('../../lib/diff.js', { ...mocks, @@ -355,9 +372,10 @@ t.test('single arg', t => { }, }) - npm.flatOptions.global = true - npm.flatOptions.diff = ['lorem'] - npm.flatOptions.prefix = resolve(path, 'project') + config.global = true + flatOptions.global = true + config.diff = ['lorem'] + npm.prefix = resolve(path, 'project') npm.globalDir = resolve(path, 'globalDir/lib/node_modules') const Diff = requireInject('../../lib/diff.js', { @@ -409,8 +427,8 @@ t.test('single arg', t => { t.equal(b, 'bar@2.0.0', 'should have expected comparison spec') } - npm.flatOptions.diff = ['bar@2.0.0'] - npm.flatOptions.prefix = path + config.diff = ['bar@2.0.0'] + npm.prefix = path diff.exec([], err => { if (err) @@ -466,8 +484,8 @@ t.test('single arg', t => { }) const diff = new Diff(npm) - npm.flatOptions.diff = ['lorem'] - npm.flatOptions.prefix = path + config.diff = ['lorem'] + npm.prefix = path diff.exec([], err => { if (err) @@ -499,8 +517,8 @@ t.test('single arg', t => { }) const diff = new Diff(npm) - npm.flatOptions.diff = ['lorem'] - npm.flatOptions.prefix = path + config.diff = ['lorem'] + npm.prefix = path diff.exec([], err => { if (err) @@ -518,8 +536,8 @@ t.test('single arg', t => { t.equal(b, `file:${path}`, 'should compare to cwd') } - npm.flatOptions.diff = ['bar'] - npm.flatOptions.prefix = path + config.diff = ['bar'] + npm.prefix = path diff.exec([], err => { if (err) @@ -537,8 +555,8 @@ t.test('single arg', t => { t.equal(b, `file:${path}`, 'should compare to cwd') } - npm.flatOptions.diff = ['my-project'] - npm.flatOptions.prefix = path + config.diff = ['my-project'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -555,8 +573,8 @@ t.test('single arg', t => { t.equal(b, `file:${path}`, 'should compare to cwd') } - npm.flatOptions.diff = ['/path/to/other-dir'] - npm.flatOptions.prefix = path + config.diff = ['/path/to/other-dir'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -566,7 +584,7 @@ t.test('single arg', t => { t.test('unsupported spec type', t => { rlp = async () => 'my-project' - npm.flatOptions.diff = ['git+https://github.com/user/foo'] + config.diff = ['git+https://github.com/user/foo'] diff.exec([], err => { t.match( @@ -591,7 +609,7 @@ t.test('first arg is a qualified spec', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.diff = ['bar@1.0.0', 'bar@^2.0.0'] + config.diff = ['bar@1.0.0', 'bar@^2.0.0'] diff.exec([], err => { if (err) throw err @@ -624,8 +642,8 @@ t.test('first arg is a qualified spec', t => { t.equal(b, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar@2.0.0', 'bar'] + npm.prefix = path + config.diff = ['bar@2.0.0', 'bar'] diff.exec([], err => { if (err) throw err @@ -635,7 +653,7 @@ t.test('first arg is a qualified spec', t => { t.test('second arg is a valid semver version', t => { t.plan(2) - npm.flatOptions.diff = ['bar@1.0.0', '2.0.0'] + config.diff = ['bar@1.0.0', '2.0.0'] libnpmdiff = async ([a, b], opts) => { t.equal(a, 'bar@1.0.0', 'should set expected first spec') @@ -656,7 +674,7 @@ t.test('first arg is a qualified spec', t => { t.equal(b, 'bar-fork@latest', 'should target latest tag if not a dep') } - npm.flatOptions.diff = ['bar@1.0.0', 'bar-fork'] + config.diff = ['bar@1.0.0', 'bar-fork'] diff.exec([], err => { if (err) throw err @@ -693,8 +711,8 @@ t.test('first arg is a known dependency name', t => { t.equal(b, 'bar@2.0.0', 'should set expected second spec') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar', 'bar@2.0.0'] + npm.prefix = path + config.diff = ['bar', 'bar@2.0.0'] diff.exec([], err => { if (err) throw err @@ -733,8 +751,8 @@ t.test('first arg is a known dependency name', t => { t.equal(b, `bar-fork@file:${resolve(path, 'node_modules/bar-fork')}`, 'should target fork local node_modules pkg') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar', 'bar-fork'] + npm.prefix = path + config.diff = ['bar', 'bar-fork'] diff.exec([], err => { if (err) throw err @@ -767,8 +785,8 @@ t.test('first arg is a known dependency name', t => { t.equal(b, 'bar@2.0.0', 'should use package name from first arg') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar', '2.0.0'] + npm.prefix = path + config.diff = ['bar', '2.0.0'] diff.exec([], err => { if (err) throw err @@ -801,8 +819,8 @@ t.test('first arg is a known dependency name', t => { t.equal(b, 'bar-fork@latest', 'should set expected second spec') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar', 'bar-fork'] + npm.prefix = path + config.diff = ['bar', 'bar-fork'] diff.exec([], err => { if (err) throw err @@ -816,7 +834,7 @@ t.test('first arg is a valid semver range', t => { t.test('second arg is a qualified spec', t => { t.plan(2) - npm.flatOptions.diff = ['1.0.0', 'bar@2.0.0'] + config.diff = ['1.0.0', 'bar@2.0.0'] libnpmdiff = async ([a, b], opts) => { t.equal(a, 'bar@1.0.0', 'should use name from second arg') @@ -855,8 +873,8 @@ t.test('first arg is a valid semver range', t => { t.equal(b, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should set expected second spec from nm') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['1.0.0', 'bar'] + npm.prefix = path + config.diff = ['1.0.0', 'bar'] diff.exec([], err => { if (err) throw err @@ -872,7 +890,7 @@ t.test('first arg is a valid semver range', t => { t.equal(b, 'my-project@2.0.0', 'should use name from project dir') } - npm.flatOptions.diff = ['1.0.0', '2.0.0'] + config.diff = ['1.0.0', '2.0.0'] diff.exec([], err => { if (err) throw err @@ -885,8 +903,8 @@ t.test('first arg is a valid semver range', t => { throw new Error('ERR') } - npm.flatOptions.diff = ['1.0.0', '2.0.0'] - npm.flatOptions.prefix = path + config.diff = ['1.0.0', '2.0.0'] + npm.prefix = path diff.exec([], err => { t.match( err, @@ -906,7 +924,7 @@ t.test('first arg is a valid semver range', t => { t.equal(b, 'bar@latest', 'should compare against latest tag') } - npm.flatOptions.diff = ['1.0.0', 'bar'] + config.diff = ['1.0.0', 'bar'] diff.exec([], err => { if (err) throw err @@ -937,8 +955,8 @@ t.test('first arg is a valid semver range', t => { }) const diff = new Diff(npm) - npm.flatOptions.diff = ['1.0.0', 'lorem@2.0.0'] - npm.flatOptions.prefix = path + config.diff = ['1.0.0', 'lorem@2.0.0'] + npm.prefix = path diff.exec([], err => { if (err) @@ -960,7 +978,7 @@ t.test('first arg is an unknown dependency name', t => { t.match(opts, { where: '.' }, 'should forward pacote options') } - npm.flatOptions.diff = ['bar', 'bar@2.0.0'] + config.diff = ['bar', 'bar@2.0.0'] diff.exec([], err => { if (err) throw err @@ -993,8 +1011,8 @@ t.test('first arg is an unknown dependency name', t => { t.equal(b, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar-fork', 'bar'] + npm.prefix = path + config.diff = ['bar-fork', 'bar'] diff.exec([], err => { if (err) throw err @@ -1009,7 +1027,7 @@ t.test('first arg is an unknown dependency name', t => { t.equal(b, 'bar@^1.0.0', 'should use name from first arg') } - npm.flatOptions.diff = ['bar', '^1.0.0'] + config.diff = ['bar', '^1.0.0'] diff.exec([], err => { if (err) throw err @@ -1024,7 +1042,7 @@ t.test('first arg is an unknown dependency name', t => { t.equal(b, 'bar-fork@latest', 'should use latest tag') } - npm.flatOptions.diff = ['bar', 'bar-fork'] + config.diff = ['bar', 'bar-fork'] diff.exec([], err => { if (err) throw err @@ -1043,8 +1061,8 @@ t.test('first arg is an unknown dependency name', t => { t.equal(b, 'bar-fork@latest', 'should use latest tag') } - npm.flatOptions.diff = ['bar', 'bar-fork'] - npm.flatOptions.prefix = path + config.diff = ['bar', 'bar-fork'] + npm.prefix = path diff.exec([], err => { if (err) @@ -1059,7 +1077,7 @@ t.test('various options', t => { t.test('using --name-only option', t => { t.plan(1) - npm.flatOptions.diffNameOnly = true + flatOptions.diffNameOnly = true libnpmdiff = async ([a, b], opts) => { t.match(opts, { @@ -1077,7 +1095,7 @@ t.test('various options', t => { t.test('set files after both versions', t => { t.plan(3) - npm.flatOptions.diff = ['2.1.4', '3.0.0'] + config.diff = ['2.1.4', '3.0.0'] libnpmdiff = async ([a, b], opts) => { t.equal(a, 'foo@2.1.4', 'should use expected spec') @@ -1114,7 +1132,7 @@ t.test('various options', t => { }, 'should forward all remaining items as filenames') } - npm.flatOptions.prefix = path + npm.prefix = path diff.exec(['./foo.js', './bar.js'], err => { if (err) throw err @@ -1124,12 +1142,12 @@ t.test('various options', t => { t.test('using diff option', t => { t.plan(1) - npm.flatOptions.diffContext = 5 - npm.flatOptions.diffIgnoreWhitespace = true - npm.flatOptions.diffNoPrefix = false - npm.flatOptions.diffSrcPrefix = 'foo/' - npm.flatOptions.diffDstPrefix = 'bar/' - npm.flatOptions.diffText = true + flatOptions.diffContext = 5 + flatOptions.diffIgnoreWhitespace = true + flatOptions.diffNoPrefix = false + flatOptions.diffSrcPrefix = 'foo/' + flatOptions.diffDstPrefix = 'bar/' + flatOptions.diffText = true libnpmdiff = async ([a, b], opts) => { t.match(opts, { @@ -1153,7 +1171,7 @@ t.test('various options', t => { }) t.test('too many args', t => { - npm.flatOptions.diff = ['a', 'b', 'c'] + config.diff = ['a', 'b', 'c'] diff.exec([], err => { t.match( err, diff --git a/deps/npm/test/lib/dist-tag.js b/deps/npm/test/lib/dist-tag.js index a3c05bb2b3..9415dacbe4 100644 --- a/deps/npm/test/lib/dist-tag.js +++ b/deps/npm/test/lib/dist-tag.js @@ -1,18 +1,10 @@ const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const { test } = require('tap') -let prefix let result = '' let log = '' -// these declared opts are used in ./utils/read-local-package.js -const _flatOptions = { - global: false, - get prefix () { - return prefix - }, -} - const routeMap = { '/-/package/@scoped%2fpkg/dist-tags': { latest: '1.0.0', @@ -60,20 +52,18 @@ const DistTag = requireInject('../../lib/dist-tag.js', { }, }) -const distTag = new DistTag({ - flatOptions: _flatOptions, +const npm = mockNpm({ config: { - get (key) { - return _flatOptions[key] - }, + global: false, }, output: msg => { result = msg }, }) +const distTag = new DistTag(npm) test('ls in current package', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: '@scoped/pkg', }), @@ -91,7 +81,7 @@ test('ls in current package', (t) => { }) test('no args in current package', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: '@scoped/pkg', }), @@ -109,7 +99,7 @@ test('no args in current package', (t) => { }) test('borked cmd usage', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['borked', '@scoped/pkg'], (err) => { t.matchSnapshot(err, 'should show usage error') result = '' @@ -119,7 +109,7 @@ test('borked cmd usage', (t) => { }) test('ls on named package', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['ls', '@scoped/another'], (err) => { t.ifError(err, 'npm dist-tags ls') t.matchSnapshot( @@ -133,7 +123,7 @@ test('ls on named package', (t) => { }) test('ls on missing package', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['ls', 'foo'], (err) => { t.matchSnapshot( log, @@ -150,7 +140,7 @@ test('ls on missing package', (t) => { }) test('ls on missing name in current package', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ version: '1.0.0', }), @@ -167,7 +157,7 @@ test('ls on missing name in current package', (t) => { }) test('only named package arg', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['@scoped/another'], (err) => { t.ifError(err, 'npm dist-tags ls') t.matchSnapshot( @@ -186,7 +176,7 @@ test('add new tag', (t) => { t.equal(opts.method, 'PUT', 'should trigger request to add new tag') t.equal(opts.body, '7.7.7', 'should point to expected version') } - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['add', '@scoped/another@7.7.7', 'c'], (err) => { t.ifError(err, 'npm dist-tags add') t.matchSnapshot( @@ -201,7 +191,7 @@ test('add new tag', (t) => { }) test('add using valid semver range as name', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['add', '@scoped/another@7.7.7', '1.0.0'], (err) => { t.match( err, @@ -219,7 +209,7 @@ test('add using valid semver range as name', (t) => { }) test('add missing args', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['add', '@scoped/another@7.7.7'], (err) => { t.matchSnapshot(err, 'should exit usage error message') result = '' @@ -229,7 +219,7 @@ test('add missing args', (t) => { }) test('add missing pkg name', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['add', null], (err) => { t.matchSnapshot(err, 'should exit usage error message') result = '' @@ -239,7 +229,7 @@ test('add missing pkg name', (t) => { }) test('set existing version', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['set', '@scoped/another@0.6.0', 'b'], (err) => { t.ifError(err, 'npm dist-tags set') t.matchSnapshot( @@ -256,7 +246,7 @@ test('remove existing tag', (t) => { npmRegistryFetchMock = async (url, opts) => { t.equal(opts.method, 'DELETE', 'should trigger request to remove tag') } - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['rm', '@scoped/another', 'c'], (err) => { t.ifError(err, 'npm dist-tags rm') t.matchSnapshot(log, 'should log remove info') @@ -269,7 +259,7 @@ test('remove existing tag', (t) => { }) test('remove non-existing tag', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['rm', '@scoped/another', 'nonexistent'], (err) => { t.match( err, @@ -284,7 +274,7 @@ test('remove non-existing tag', (t) => { }) test('remove missing pkg name', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['rm', null], (err) => { t.matchSnapshot(err, 'should exit usage error message') result = '' diff --git a/deps/npm/test/lib/exec.js b/deps/npm/test/lib/exec.js index eb9fef6a61..bcfe75577c 100644 --- a/deps/npm/test/lib/exec.js +++ b/deps/npm/test/lib/exec.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const { resolve, delimiter } = require('path') const OUTPUT = [] const output = (...msg) => OUTPUT.push(msg) @@ -25,25 +26,23 @@ class Arborist { let PROGRESS_ENABLED = true const LOG_WARN = [] let PROGRESS_IGNORED = false -const npm = { - flatOptions: { - yes: true, - call: '', - package: [], - legacyPeerDeps: false, - shell: 'shell-cmd', - }, +const flatOptions = { + legacyPeerDeps: false, + package: [], +} +const config = { + cache: 'cache-dir', + yes: true, + call: '', + package: [], + shell: 'shell-cmd', +} +const npm = mockNpm({ + flatOptions, + config, localPrefix: 'local-prefix', localBin: 'local-bin', globalBin: 'global-bin', - config: { - get: k => { - if (k !== 'cache') - throw new Error('unexpected config get') - - return 'cache-dir' - }, - }, log: { disableProgress: () => { PROGRESS_ENABLED = false @@ -56,7 +55,7 @@ const npm = { }, }, output, -} +}) const RUN_SCRIPTS = [] const runScript = async opt => { @@ -108,9 +107,12 @@ t.afterEach(cb => { READ_ERROR = null LOG_WARN.length = 0 PROGRESS_IGNORED = false - npm.flatOptions.legacyPeerDeps = false - npm.flatOptions.package = [] - npm.flatOptions.call = '' + flatOptions.legacyPeerDeps = false + config.color = false + config.package = [] + flatOptions.package = [] + config.call = '' + config.yes = true npm.localBin = 'local-bin' npm.globalBin = 'global-bin' cb() @@ -186,7 +188,7 @@ t.test('npm exec foo, already present locally', t => { if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -240,14 +242,27 @@ t.test('npm exec <noargs>, run interactive shell', t => { cb() }) } - t.test('print message when tty and not in CI', t => { CI_NAME = null process.stdin.isTTY = true run(t, true, () => { t.strictSame(LOG_WARN, []) t.strictSame(OUTPUT, [ - ['\nEntering npm script environment\nType \'exit\' or ^D when finished\n'], + [`\nEntering npm script environment at location:\n${process.cwd()}\nType 'exit' or ^D when finished\n`], + ], 'printed message about interactive shell') + t.end() + }) + }) + + t.test('print message with color when tty and not in CI', t => { + CI_NAME = null + process.stdin.isTTY = true + config.color = true + + run(t, true, () => { + t.strictSame(LOG_WARN, []) + t.strictSame(OUTPUT, [ + [`\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() }) @@ -300,7 +315,7 @@ t.test('npm exec foo, not present locally or in central loc', t => { if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add: ['foo@'], legacyPeerDeps: false}], 'need to install foo@') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -340,7 +355,7 @@ t.test('npm exec foo, not present locally but in central loc', t => { if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [], 'no need to install again, already there') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -380,7 +395,7 @@ t.test('npm exec foo, present locally but wrong version', t => { if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{ add: ['foo@2.x'], legacyPeerDeps: false }], 'need to add foo@2.x') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -412,12 +427,13 @@ t.test('npm exec --package=foo bar', t => { }, _from: 'foo@', } - npm.flatOptions.package = ['foo'] + config.package = ['foo'] + flatOptions.package = ['foo'] exec.exec(['bar', 'one arg', 'two arg'], er => { if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -459,7 +475,7 @@ t.test('npm exec @foo/bar -- --some=arg, locally installed', t => { if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['@foo/bar'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -502,7 +518,7 @@ t.test('npm exec @foo/bar, with same bin alias and no unscoped named bin, locall if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['@foo/bar'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -552,7 +568,7 @@ t.test('run command with 2 packages, need install, verify sort', t => { t.plan(cases.length) for (const packages of cases) { t.test(packages.join(', '), t => { - npm.flatOptions.package = packages + config.package = packages const add = packages.map(p => `${p}@`).sort((a, b) => a.localeCompare(b)) const path = t.testdir() const installDir = resolve('cache-dir/_npx/07de77790e5f40f2') @@ -583,7 +599,7 @@ t.test('run command with 2 packages, need install, verify sort', t => { if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -652,8 +668,8 @@ t.test('npm exec foo, many bins in package, none named foo', t => { t.test('npm exec -p foo -c "ls -laF"', t => { const path = t.testdir() npm.localPrefix = path - npm.flatOptions.package = ['foo'] - npm.flatOptions.call = 'ls -laF' + config.package = ['foo'] + config.call = 'ls -laF' ARB_ACTUAL_TREE[path] = { children: new Map([['foo', { name: 'foo', version: '1.2.3' }]]), } @@ -666,7 +682,7 @@ t.test('npm exec -p foo -c "ls -laF"', t => { if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -683,7 +699,7 @@ t.test('npm exec -p foo -c "ls -laF"', t => { }) t.test('positional args and --call together is an error', t => { - npm.flatOptions.call = 'true' + config.call = 'true' exec.exec(['foo'], er => { t.equal(er, exec.usage) t.end() @@ -705,8 +721,8 @@ t.test('prompt when installs are needed if not already present and shell is a TT const packages = ['foo', 'bar'] READ_RESULT = 'yolo' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const add = packages.map(p => `${p}@`).sort((a, b) => a.localeCompare(b)) const path = t.testdir() @@ -738,7 +754,7 @@ t.test('prompt when installs are needed if not already present and shell is a TT if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -774,8 +790,8 @@ t.test('skip prompt when installs are needed if not already present and shell is const packages = ['foo', 'bar'] READ_RESULT = 'yolo' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const add = packages.map(p => `${p}@`).sort((a, b) => a.localeCompare(b)) const path = t.testdir() @@ -807,7 +823,7 @@ t.test('skip prompt when installs are needed if not already present and shell is if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -841,8 +857,8 @@ t.test('skip prompt when installs are needed if not already present and shell is const packages = ['foo'] READ_RESULT = 'yolo' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const add = packages.map(p => `${p}@`).sort((a, b) => a.localeCompare(b)) const path = t.testdir() @@ -866,7 +882,7 @@ t.test('skip prompt when installs are needed if not already present and shell is if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install the package') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -900,8 +916,8 @@ t.test('abort if prompt rejected', t => { const packages = ['foo', 'bar'] READ_RESULT = 'no, why would I want such a thing??' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const path = t.testdir() const installDir = resolve('cache-dir/_npx/07de77790e5f40f2') @@ -929,9 +945,9 @@ t.test('abort if prompt rejected', t => { _from: 'bar@', } exec.exec(['foobar'], er => { - t.equal(er, 'canceled', 'should be canceled') + t.match(er, /canceled/, 'should be canceled') t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no install performed') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.strictSame(RUN_SCRIPTS, []) @@ -958,8 +974,8 @@ t.test('abort if prompt false', t => { const packages = ['foo', 'bar'] READ_ERROR = 'canceled' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const path = t.testdir() const installDir = resolve('cache-dir/_npx/07de77790e5f40f2') @@ -989,7 +1005,7 @@ t.test('abort if prompt false', t => { exec.exec(['foobar'], er => { t.equal(er, 'canceled', 'should be canceled') t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no install performed') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.strictSame(RUN_SCRIPTS, []) @@ -1015,8 +1031,8 @@ t.test('abort if -n provided', t => { const packages = ['foo', 'bar'] - npm.flatOptions.package = packages - npm.flatOptions.yes = false + config.package = packages + config.yes = false const path = t.testdir() const installDir = resolve('cache-dir/_npx/07de77790e5f40f2') @@ -1044,9 +1060,9 @@ t.test('abort if -n provided', t => { _from: 'bar@', } exec.exec(['foobar'], er => { - t.equal(er, 'canceled', 'should be canceled') + t.match(er, /canceled/, 'should be canceled') t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no install performed') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.strictSame(RUN_SCRIPTS, []) @@ -1073,8 +1089,8 @@ t.test('forward legacyPeerDeps opt', t => { }, _from: 'foo@', } - npm.flatOptions.yes = true - npm.flatOptions.legacyPeerDeps = true + config.yes = true + flatOptions.legacyPeerDeps = true exec.exec(['foo'], er => { if (er) throw er @@ -1082,3 +1098,93 @@ t.test('forward legacyPeerDeps opt', t => { t.done() }) }) + +t.test('workspaces', t => { + npm.localPrefix = t.testdir({ + node_modules: { + '.bin': { + foo: '', + }, + }, + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + bin: 'cli.js', + }), + 'cli.js': '', + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '1.0.0', + }), + }, + }, + 'package.json': JSON.stringify({ + name: 'root', + version: '1.0.0', + workspaces: ['packages/*'], + }), + }) + + PROGRESS_IGNORED = true + npm.localBin = resolve(npm.localPrefix, 'node_modules/.bin') + + t.test('with args, run scripts in the context of a workspace', t => { + exec.execWorkspaces(['foo', 'one arg', 'two arg'], ['a', 'b'], er => { + if (er) + throw er + + t.match(RUN_SCRIPTS, [{ + pkg: { scripts: { npx: 'foo' }}, + args: ['one arg', 'two arg'], + banner: false, + path: process.cwd(), + stdioString: true, + event: 'npx', + env: { + PATH: [npm.localBin, ...PATH].join(delimiter), + }, + stdio: 'inherit', + }]) + t.end() + }) + }) + + t.test('no args, spawn interactive shell', async t => { + CI_NAME = null + process.stdin.isTTY = true + + await new Promise((res, rej) => { + exec.execWorkspaces([], ['a'], er => { + if (er) + return rej(er) + + t.strictSame(LOG_WARN, []) + t.strictSame(OUTPUT, [ + [`\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() + }) + }) + + config.color = true + OUTPUT.length = 0 + await new Promise((res, rej) => { + exec.execWorkspaces([], ['a'], er => { + if (er) + return rej(er) + + t.strictSame(LOG_WARN, []) + t.strictSame(OUTPUT, [ + [`\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() + }) + }) + }) + + t.end() +}) diff --git a/deps/npm/test/lib/fund.js b/deps/npm/test/lib/fund.js index 2ae604a653..8c10007844 100644 --- a/deps/npm/test/lib/fund.js +++ b/deps/npm/test/lib/fund.js @@ -1,5 +1,6 @@ const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const version = '1.0.0' const funding = { @@ -180,19 +181,18 @@ const conflictingFundingPackages = { let result = '' let printUrl = '' -const _flatOptions = { +const config = { color: false, json: false, global: false, - prefix: undefined, unicode: false, - which: undefined, + which: null, } const openUrl = async (npm, url, msg) => { if (url === 'http://npmjs.org') throw new Error('ERROR') - if (_flatOptions.json) { + if (config.json) { printUrl = JSON.stringify({ title: msg, url: url, @@ -210,18 +210,16 @@ const Fund = requireInject('../../lib/fund.js', { : Promise.reject(new Error('ERROR')), }, }) -const fund = new Fund({ - flatOptions: _flatOptions, - get prefix () { - return _flatOptions.prefix - }, +const npm = mockNpm({ + config, output: msg => { result += msg + '\n' }, }) +const fund = new Fund(npm) test('fund with no package containing funding', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'no-funding-package', version: '0.0.0', @@ -237,7 +235,7 @@ test('fund with no package containing funding', t => { }) test('fund in which same maintainer owns all its deps', t => { - _flatOptions.prefix = t.testdir(maintainerOwnsAllDeps) + npm.prefix = t.testdir(maintainerOwnsAllDeps) fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -248,8 +246,8 @@ test('fund in which same maintainer owns all its deps', t => { }) test('fund in which same maintainer owns all its deps, using --json option', t => { - _flatOptions.json = true - _flatOptions.prefix = t.testdir(maintainerOwnsAllDeps) + config.json = true + npm.prefix = t.testdir(maintainerOwnsAllDeps) fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -281,13 +279,13 @@ test('fund in which same maintainer owns all its deps, using --json option', t = ) result = '' - _flatOptions.json = false + config.json = false t.end() }) }) test('fund containing multi-level nested deps with no funding', t => { - _flatOptions.prefix = t.testdir(nestedNoFundingPackages) + npm.prefix = t.testdir(nestedNoFundingPackages) fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -302,8 +300,8 @@ test('fund containing multi-level nested deps with no funding', t => { }) test('fund containing multi-level nested deps with no funding, using --json option', t => { - _flatOptions.prefix = t.testdir(nestedNoFundingPackages) - _flatOptions.json = true + npm.prefix = t.testdir(nestedNoFundingPackages) + config.json = true fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -328,14 +326,14 @@ test('fund containing multi-level nested deps with no funding, using --json opti ) result = '' - _flatOptions.json = false + config.json = false t.end() }) }) test('fund containing multi-level nested deps with no funding, using --json option', t => { - _flatOptions.prefix = t.testdir(nestedMultipleFundingPackages) - _flatOptions.json = true + npm.prefix = t.testdir(nestedMultipleFundingPackages) + config.json = true fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -385,26 +383,26 @@ test('fund containing multi-level nested deps with no funding, using --json opti ) result = '' - _flatOptions.json = false + config.json = false t.end() }) }) test('fund does not support global', t => { - _flatOptions.prefix = t.testdir({}) - _flatOptions.global = true + npm.prefix = t.testdir({}) + config.global = true fund.exec([], (err) => { t.match(err.code, 'EFUNDGLOBAL', 'should throw EFUNDGLOBAL error') result = '' - _flatOptions.global = false + config.global = false t.end() }) }) test('fund using package argument', t => { - _flatOptions.prefix = t.testdir(maintainerOwnsAllDeps) + npm.prefix = t.testdir(maintainerOwnsAllDeps) fund.exec(['.'], (err) => { t.ifError(err, 'should not error out') @@ -416,9 +414,9 @@ test('fund using package argument', t => { }) test('fund does not support global, using --json option', t => { - _flatOptions.prefix = t.testdir({}) - _flatOptions.global = true - _flatOptions.json = true + npm.prefix = t.testdir({}) + config.global = true + config.json = true fund.exec([], (err) => { t.equal(err.code, 'EFUNDGLOBAL', 'should use EFUNDGLOBAL error code') @@ -428,14 +426,14 @@ test('fund does not support global, using --json option', t => { 'should use expected error msg' ) - _flatOptions.global = false - _flatOptions.json = false + config.global = false + config.json = false t.end() }) }) test('fund using string shorthand', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'funding-string-shorthand', version: '0.0.0', @@ -453,7 +451,7 @@ test('fund using string shorthand', t => { }) test('fund using nested packages with multiple sources', t => { - _flatOptions.prefix = t.testdir(nestedMultipleFundingPackages) + npm.prefix = t.testdir(nestedMultipleFundingPackages) fund.exec(['.'], (err) => { t.ifError(err, 'should not error out') @@ -465,7 +463,7 @@ test('fund using nested packages with multiple sources', t => { }) test('fund using symlink ref', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'using-symlink-ref', version: '1.0.0', @@ -511,7 +509,7 @@ test('fund using symlink ref', t => { }) test('fund using data from actual tree', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'using-actual-tree', version: '1.0.0', @@ -558,22 +556,22 @@ test('fund using data from actual tree', t => { }) test('fund using nested packages with multiple sources, with a source number', t => { - _flatOptions.prefix = t.testdir(nestedMultipleFundingPackages) - _flatOptions.which = '1' + npm.prefix = t.testdir(nestedMultipleFundingPackages) + config.which = '1' fund.exec(['.'], (err) => { t.ifError(err, 'should not error out') t.matchSnapshot(printUrl, 'should open the numbered URL') - _flatOptions.which = undefined + config.which = null printUrl = '' t.end() }) }) test('fund using pkg name while having conflicting versions', t => { - _flatOptions.prefix = t.testdir(conflictingFundingPackages) - _flatOptions.which = '1' + npm.prefix = t.testdir(conflictingFundingPackages) + config.which = '1' fund.exec(['foo'], (err) => { t.ifError(err, 'should not error out') @@ -585,8 +583,8 @@ test('fund using pkg name while having conflicting versions', t => { }) test('fund using package argument with no browser, using --json option', t => { - _flatOptions.prefix = t.testdir(maintainerOwnsAllDeps) - _flatOptions.json = true + npm.prefix = t.testdir(maintainerOwnsAllDeps) + config.json = true fund.exec(['.'], (err) => { t.ifError(err, 'should not error out') @@ -599,14 +597,14 @@ test('fund using package argument with no browser, using --json option', t => { 'should open funding url using json output' ) - _flatOptions.json = false + config.json = false printUrl = '' t.end() }) }) test('fund using package info fetch from registry', t => { - _flatOptions.prefix = t.testdir({}) + npm.prefix = t.testdir({}) fund.exec(['ntl'], (err) => { t.ifError(err, 'should not error out') @@ -622,7 +620,7 @@ test('fund using package info fetch from registry', t => { }) test('fund tries to use package info fetch from registry but registry has nothing', t => { - _flatOptions.prefix = t.testdir({}) + npm.prefix = t.testdir({}) fund.exec(['foo'], (err) => { t.equal(err.code, 'ENOFUND', 'should have ENOFUND error code') @@ -638,7 +636,7 @@ test('fund tries to use package info fetch from registry but registry has nothin }) test('fund but target module has no funding info', t => { - _flatOptions.prefix = t.testdir(nestedNoFundingPackages) + npm.prefix = t.testdir(nestedNoFundingPackages) fund.exec(['foo'], (err) => { t.equal(err.code, 'ENOFUND', 'should have ENOFUND error code') @@ -654,8 +652,8 @@ test('fund but target module has no funding info', t => { }) test('fund using bad which value', t => { - _flatOptions.prefix = t.testdir(nestedMultipleFundingPackages) - _flatOptions.which = 3 + npm.prefix = t.testdir(nestedMultipleFundingPackages) + config.which = 3 fund.exec(['bar'], (err) => { t.equal(err.code, 'EFUNDNUMBER', 'should have EFUNDNUMBER error code') @@ -665,14 +663,14 @@ test('fund using bad which value', t => { 'should have bad which option error message' ) - _flatOptions.which = undefined + config.which = null result = '' t.end() }) }) test('fund pkg missing version number', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'foo', funding: 'http://example.com/foo', @@ -688,7 +686,7 @@ test('fund pkg missing version number', t => { }) test('fund a package throws on openUrl', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'foo', version: '1.0.0', @@ -704,7 +702,7 @@ test('fund a package throws on openUrl', t => { }) test('fund a package with type and multiple sources', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'foo', funding: [ @@ -730,7 +728,7 @@ test('fund a package with type and multiple sources', t => { }) test('fund colors', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-fund-colors', version: '1.0.0', @@ -782,20 +780,20 @@ test('fund colors', t => { }, }, }) - _flatOptions.color = true + npm.color = true fund.exec([], (err) => { t.ifError(err, 'should not error out') t.matchSnapshot(result, 'should print output with color info') result = '' - _flatOptions.color = false + npm.color = false t.end() }) }) test('sub dep with fund info and a parent with no funding info', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-multiple-funding-sources', version: '1.0.0', diff --git a/deps/npm/test/lib/help-search.js b/deps/npm/test/lib/help-search.js index 6228f5ca97..567097a2eb 100644 --- a/deps/npm/test/lib/help-search.js +++ b/deps/npm/test/lib/help-search.js @@ -1,6 +1,7 @@ const { test } = require('tap') const { join } = require('path') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const ansicolors = require('ansicolors') const OUTPUT = [] @@ -8,26 +9,24 @@ const output = (msg) => { OUTPUT.push(msg) } -let npmHelpArgs = null -let npmHelpErr = null -const npm = { +const config = { + long: false, +} +const npmHelpErr = null +const npm = mockNpm({ color: false, + config, flatOptions: { long: false, }, + usage: 'npm test usage', commands: { help: (args, cb) => { - npmHelpArgs = args return cb(npmHelpErr) }, }, output, -} - -let npmUsageArg = null -const npmUsage = (npm, arg) => { - npmUsageArg = arg -} +}) let globRoot = null const globDir = { @@ -45,7 +44,6 @@ const glob = (p, cb) => cb(null, Object.keys(globDir).map((file) => join(globRoot, file))) const HelpSearch = requireInject('../../lib/help-search.js', { - '../../lib/utils/npm-usage.js': npmUsage, glob, }) const helpSearch = new HelpSearch(npm) @@ -61,8 +59,7 @@ test('npm help-search', t => { if (err) throw err - t.match(OUTPUT, /Top hits for/, 'outputs results') - t.match(OUTPUT, /Did you mean this\?\n\s+exec/, 'matched command, so suggest it') + t.match(OUTPUT, /Top hits for "exec"/, 'outputs results') t.end() }) }) @@ -84,46 +81,12 @@ test('npm help-search multiple terms', t => { }) }) -test('npm help-search single result prints full section', t => { - globRoot = t.testdir(globDir) - t.teardown(() => { - OUTPUT.length = 0 - npmHelpArgs = null - globRoot = null - }) - - return helpSearch.exec(['does not exist in'], (err) => { - if (err) - throw err - - t.strictSame(npmHelpArgs, ['npm-install'], 'identified the correct man page and called help with it') - t.end() - }) -}) - -test('npm help-search single result propagates error', t => { - globRoot = t.testdir(globDir) - npmHelpErr = new Error('help broke') - t.teardown(() => { - OUTPUT.length = 0 - npmHelpArgs = null - npmHelpErr = null - globRoot = null - }) - - return helpSearch.exec(['does not exist in'], (err) => { - t.strictSame(npmHelpArgs, ['npm-install'], 'identified the correct man page and called help with it') - t.match(err, /help broke/, 'propagated the error from help') - t.end() - }) -}) - test('npm help-search long output', t => { globRoot = t.testdir(globDir) - npm.flatOptions.long = true + config.long = true t.teardown(() => { OUTPUT.length = 0 - npm.flatOptions.long = false + config.long = false globRoot = null }) @@ -138,11 +101,11 @@ test('npm help-search long output', t => { test('npm help-search long output with color', t => { globRoot = t.testdir(globDir) - npm.flatOptions.long = true + config.long = true npm.color = true t.teardown(() => { OUTPUT.length = 0 - npm.flatOptions.long = false + config.long = false npm.color = false globRoot = null }) @@ -159,7 +122,8 @@ test('npm help-search long output with color', t => { test('npm help-search no args', t => { return helpSearch.exec([], (err) => { - t.match(err, /npm help-search/, 'throws usage') + t.notOk(err) + t.match(OUTPUT, /npm help-search/, 'outputs usage') t.end() }) }) @@ -168,7 +132,6 @@ test('npm help-search no matches', t => { globRoot = t.testdir(globDir) t.teardown(() => { OUTPUT.length = 0 - npmUsageArg = null globRoot = null }) @@ -176,7 +139,7 @@ test('npm help-search no matches', t => { if (err) throw err - t.equal(npmUsageArg, false, 'called npmUsage for no matches') + t.match(OUTPUT, /No matches/) t.end() }) }) diff --git a/deps/npm/test/lib/help.js b/deps/npm/test/lib/help.js index ae2f7e99da..ccf13a7e46 100644 --- a/deps/npm/test/lib/help.js +++ b/deps/npm/test/lib/help.js @@ -2,11 +2,6 @@ const { test } = require('tap') const requireInject = require('require-inject') const { EventEmitter } = require('events') -let npmUsageArg = null -const npmUsage = (npm, arg) => { - npmUsageArg = arg -} - const npmConfig = { usage: false, viewer: undefined, @@ -16,6 +11,7 @@ const npmConfig = { let helpSearchArgs = null const OUTPUT = [] const npm = { + usage: 'test npm usage', config: { get: (key) => npmConfig[key], set: (key, value) => { @@ -48,7 +44,9 @@ const globDefaults = [ let globErr = null let globResult = globDefaults +let globParam const glob = (p, cb) => { + globParam = p return cb(globErr, globResult) } @@ -71,7 +69,6 @@ const openUrl = async (npm, url, msg) => { } const Help = requireInject('../../lib/help.js', { - '../../lib/utils/npm-usage.js': npmUsage, '../../lib/utils/open-url.js': openUrl, child_process: { spawn, @@ -81,15 +78,11 @@ const Help = requireInject('../../lib/help.js', { const help = new Help(npm) test('npm help', t => { - t.teardown(() => { - npmUsageArg = null - }) - return help.exec([], (err) => { if (err) throw err - t.equal(npmUsageArg, false, 'called npmUsage') + t.match(OUTPUT, ['test npm usage'], 'showed npm usage') t.end() }) }) @@ -107,22 +100,6 @@ test('npm help completion', async t => { t.rejects(help.completion({ conf: { argv: { remain: [] } } }), /glob failed/, 'glob errors propagate') }) -test('npm help -h', t => { - npmConfig.usage = true - t.teardown(() => { - npmConfig.usage = false - OUTPUT.length = 0 - }) - - return help.exec(['help'], (err) => { - if (err) - throw err - - t.strictSame(OUTPUT, ['npm help <term>'], 'outputs usage information for command') - t.end() - }) -}) - test('npm help multiple args calls search', t => { t.teardown(() => { helpSearchArgs = null @@ -180,7 +157,7 @@ test('npm help whoami', t => { throw err t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['1', 'npm-whoami'], 'passes the correct arguments') + t.strictSame(spawnArgs, [globResult[0]], 'passes the correct arguments') t.end() }) }) @@ -212,12 +189,12 @@ test('npm help 5 install', t => { npmConfig.viewer = 'browser' globResult = [ '/root/man/man5/install.5', - '/root/man/man1/npm-install.1', ] t.teardown(() => { npmConfig.viewer = undefined globResult = globDefaults + globParam = null spawnBin = null spawnArgs = null }) @@ -226,6 +203,7 @@ test('npm help 5 install', t => { if (err) throw err + t.match(globParam, /man5/, 'searches only in man5 folder') t.match(openUrlArg, /configuring-npm(\/|\\)install.html$/, 'attempts to open the correct url') t.end() }) @@ -234,11 +212,11 @@ test('npm help 5 install', t => { test('npm help 7 config', t => { npmConfig.viewer = 'browser' globResult = [ - '/root/man/man1/npm-config.1', '/root/man/man7/config.7', ] t.teardown(() => { npmConfig.viewer = undefined + globParam = null globResult = globDefaults spawnBin = null spawnArgs = null @@ -248,49 +226,12 @@ test('npm help 7 config', t => { if (err) throw err + t.match(globParam, /man7/, 'searches only in man5 folder') t.match(openUrlArg, /using-npm(\/|\\)config.html$/, 'attempts to open the correct url') t.end() }) }) -test('npm help with browser viewer and invalid section throws', t => { - npmConfig.viewer = 'browser' - globResult = [ - '/root/man/man1/npm-config.1', - '/root/man/man7/config.7', - '/root/man/man9/config.9', - ] - t.teardown(() => { - npmConfig.viewer = undefined - globResult = globDefaults - spawnBin = null - spawnArgs = null - }) - - return help.exec(['9', 'config'], (err) => { - t.match(err, /invalid man section: 9/, 'throws appropriate error') - t.end() - }) -}) - -test('npm help global redirects to folders', t => { - globResult = ['/root/man/man5/folders.5'] - t.teardown(() => { - globResult = globDefaults - spawnBin = null - spawnArgs = null - }) - - return help.exec(['global'], (err) => { - if (err) - throw err - - t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['5', 'folders'], 'passes the correct arguments') - t.end() - }) -}) - test('npm help package.json redirects to package-json', t => { globResult = ['/root/man/man5/package-json.5'] t.teardown(() => { @@ -304,7 +245,8 @@ test('npm help package.json redirects to package-json', t => { throw err t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['5', 'package-json'], 'passes the correct arguments') + t.match(globParam, /package-json/, 'glob was asked to find package-json') + t.strictSame(spawnArgs, [globResult[0]], 'passes the correct arguments') t.end() }) }) @@ -327,7 +269,7 @@ test('npm help ?(un)star', t => { throw err t.equal(spawnBin, 'emacsclient', 'maps woman to emacs correctly') - t.strictSame(spawnArgs, ['-e', `(woman-find-file '/root/man/man1/npm-unstar.1')`], 'passes the correct arguments') + t.strictSame(spawnArgs, ['-e', `(woman-find-file '/root/man/man1/npm-star.1')`], 'passes the correct arguments') t.end() }) }) @@ -350,7 +292,7 @@ test('npm help - woman viewer propagates errors', t => { return help.exec(['?(un)star'], (err) => { t.match(err, /help process exited with code: 1/, 'received the correct error') t.equal(spawnBin, 'emacsclient', 'maps woman to emacs correctly') - t.strictSame(spawnArgs, ['-e', `(woman-find-file '/root/man/man1/npm-unstar.1')`], 'passes the correct arguments') + t.strictSame(spawnArgs, ['-e', `(woman-find-file '/root/man/man1/npm-star.1')`], 'passes the correct arguments') t.end() }) }) @@ -372,7 +314,7 @@ test('npm help un*', t => { throw err t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['1', 'npm-unstar'], 'passes the correct arguments') + t.strictSame(spawnArgs, ['/root/man/man1/npm-uninstall.1'], 'passes the correct arguments') t.end() }) }) @@ -394,7 +336,7 @@ test('npm help - man viewer propagates errors', t => { return help.exec(['un*'], (err) => { t.match(err, /help process exited with code: 1/, 'received correct error') t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['1', 'npm-unstar'], 'passes the correct arguments') + t.strictSame(spawnArgs, ['/root/man/man1/npm-uninstall.1'], 'passes the correct arguments') t.end() }) }) diff --git a/deps/npm/test/lib/init.js b/deps/npm/test/lib/init.js index 8b9f32e156..2b212f4a15 100644 --- a/deps/npm/test/lib/init.js +++ b/deps/npm/test/lib/init.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let result = '' const npmLog = { @@ -10,14 +11,17 @@ const npmLog = { resume: () => null, silly: () => null, } -const npm = { - config: { set () {} }, - flatOptions: {}, +const config = { + 'init-module': '~/.npm-init.js', +} +const npm = mockNpm({ + config, log: npmLog, + commands: {}, output: (...msg) => { result += msg.join('\n') }, -} +}) const mocks = { 'init-package-json': (dir, initFile, config, cb) => cb(null, 'data'), '../../lib/utils/usage.js': () => 'usage instructions', @@ -27,19 +31,13 @@ const init = new Init(npm) t.afterEach(cb => { result = '' - npm.config = { get: () => '', set () {} } + config.package = undefined npm.commands = {} - Object.defineProperty(npm, 'flatOptions', { value: {} }) npm.log = npmLog cb() }) t.test('classic npm init no args', t => { - npm.config = { - get () { - return '~/.npm-init.js' - }, - } init.exec([], err => { t.ifError(err, 'npm init no args') t.matchSnapshot(result, 'should print helper info') @@ -49,9 +47,7 @@ t.test('classic npm init no args', t => { t.test('classic npm init -y', t => { t.plan(7) - npm.config = { - get: () => '~/.npm-init.js', - } + config.yes = true Object.defineProperty(npm, 'flatOptions', { value: { yes: true} }) npm.log = { ...npm.log } npm.log.silly = (title, msg) => { @@ -72,14 +68,9 @@ t.test('classic npm init -y', t => { }) t.test('npm init <arg>', t => { - t.plan(4) - npm.config = { - set (key, val) { - t.equal(key, 'package', 'should set package key') - t.deepEqual(val, [], 'should set empty array value') - }, - } + t.plan(3) npm.commands.exec = (arr, cb) => { + t.deepEqual(config.package, [], 'should set empty array value') t.deepEqual( arr, ['create-react-app'], @@ -178,20 +169,9 @@ t.test('npm init exec error', t => { }) t.test('should not rewrite flatOptions', t => { - t.plan(4) - Object.defineProperty(npm, 'flatOptions', { - get: () => ({}), - set () { - throw new Error('Should not set flatOptions') - }, - }) - npm.config = { - set (key, val) { - t.equal(key, 'package', 'should set package key') - t.deepEqual(val, [], 'should set empty array value') - }, - } + t.plan(3) npm.commands.exec = (arr, cb) => { + t.deepEqual(config.package, [], 'should set empty array value') t.deepEqual( arr, ['create-react-app', 'my-app'], diff --git a/deps/npm/test/lib/install.js b/deps/npm/test/lib/install.js index 8b7a968511..b44452a69c 100644 --- a/deps/npm/test/lib/install.js +++ b/deps/npm/test/lib/install.js @@ -2,6 +2,7 @@ const { test } = require('tap') const Install = require('../../lib/install.js') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('should install using Arborist', (t) => { const SCRIPTS = [] @@ -28,16 +29,14 @@ test('should install using Arborist', (t) => { throw new Error('got wrong object passed to reify-finish') }, }) - const install = new Install({ + + const npm = mockNpm({ + config: { dev: true }, + flatOptions: { global: false }, globalDir: 'path/to/node_modules/', prefix: 'foo', - flatOptions: { - global: false, - }, - config: { - get: () => true, - }, }) + const install = new Install(npm) t.test('with args', t => { install.exec(['fizzbuzz'], er => { @@ -86,17 +85,16 @@ test('should ignore scripts with --ignore-scripts', (t) => { } }, }) - const install = new Install({ + const npm = mockNpm({ globalDir: 'path/to/node_modules/', prefix: 'foo', - flatOptions: { - global: false, - ignoreScripts: true, - }, + flatOptions: { global: false }, config: { - get: () => false, + global: false, + 'ignore-scripts': true, }, }) + const install = new Install(npm) install.exec([], er => { if (er) throw er @@ -113,16 +111,13 @@ test('should install globally using Arborist', (t) => { this.reify = () => {} }, }) - const install = new Install({ + const npm = mockNpm({ globalDir: 'path/to/node_modules/', prefix: 'foo', - flatOptions: { - global: true, - }, - config: { - get: () => false, - }, + config: { global: true }, + flatOptions: { global: true }, }) + const install = new Install(npm) install.exec([], er => { if (er) throw er diff --git a/deps/npm/test/lib/link.js b/deps/npm/test/lib/link.js index be7af3f524..0d96ba0bcd 100644 --- a/deps/npm/test/lib/link.js +++ b/deps/npm/test/lib/link.js @@ -3,6 +3,7 @@ const { resolve } = require('path') const Arborist = require('@npmcli/arborist') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const redactCwd = (path) => { const normalizePath = p => p @@ -15,17 +16,13 @@ const redactCwd = (path) => { t.cleanSnapshot = (str) => redactCwd(str) let reifyOutput -const npm = { +const config = {} +const npm = mockNpm({ globalDir: null, prefix: null, - flatOptions: {}, - config: { - get () { - return false - }, - find () {}, - }, -} + config, +}) + const printLinks = async (opts) => { let res = '' const arb = new Arborist(opts) diff --git a/deps/npm/test/lib/load-all-commands.js b/deps/npm/test/lib/load-all-commands.js index e31a2b9936..d7eb2eae0a 100644 --- a/deps/npm/test/lib/load-all-commands.js +++ b/deps/npm/test/lib/load-all-commands.js @@ -1,27 +1,37 @@ -// Thanks to nyc not working properly with proxies this -// doesn't affect coverage. but it does ensure that every command has a usage -// that contains its name, and if it has completion it is a function -const npm = require('../../lib/npm.js') +// Thanks to nyc not working properly with proxies this doesn't affect +// coverage. but it does ensure that every command has a usage that renders, +// contains its name, a description, and if it has completion it is a function. +// That it renders also ensures that any params we've defined in our commands +// work. +const requireInject = require('require-inject') +const npm = requireInject('../../lib/npm.js') const t = require('tap') const { cmdList } = require('../../lib/utils/cmd-list.js') -t.test('load npm', t => npm.load(er => { - if (er) - throw er -})) - +let npmOutput = [] +npm.output = (msg) => { + npmOutput = msg +} t.test('load each command', t => { - t.plan(cmdList.length) - for (const cmd of cmdList.sort((a, b) => a.localeCompare(b))) { - t.test(cmd, t => { - const impl = npm.commands[cmd] - if (impl.completion) { - t.plan(3) - t.isa(impl.completion, 'function', 'completion, if present, is a function') - } else - t.plan(2) - t.isa(impl, 'function', 'implementation is a function') - t.match(impl.usage, cmd, 'usage contains the command') - }) - } + t.plan(cmdList.length + 1) + npm.load((er) => { + t.notOk(er) + npm.config.set('usage', true) + for (const cmd of cmdList.sort((a, b) => a.localeCompare(b))) { + t.test(cmd, t => { + const impl = npm.commands[cmd] + if (impl.completion) + t.isa(impl.completion, 'function', 'completion, if present, is a function') + t.isa(impl, 'function', 'implementation is a function') + t.ok(impl.description, 'implementation has a description') + t.ok(impl.name, 'implementation has a name') + t.match(impl.usage, cmd, 'usage contains the command') + impl([], (err) => { + t.notOk(err) + t.match(npmOutput, impl.usage, 'usage is output') + t.end() + }) + }) + } + }) }) diff --git a/deps/npm/test/lib/logout.js b/deps/npm/test/lib/logout.js index b00fa641d8..bae797f969 100644 --- a/deps/npm/test/lib/logout.js +++ b/deps/npm/test/lib/logout.js @@ -1,12 +1,17 @@ const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const { test } = require('tap') -const _flatOptions = { +const config = { registry: 'https://registry.npmjs.org/', scope: '', } +const flatOptions = { + registry: 'https://registry.npmjs.org/', + scope: '', +} +const npm = mockNpm({ config, flatOptions }) -const config = {} const npmlog = {} let result = null @@ -20,15 +25,12 @@ const mocks = { } const Logout = requireInject('../../lib/logout.js', mocks) -const logout = new Logout({ - flatOptions: _flatOptions, - config, -}) +const logout = new Logout(npm) test('token logout', async (t) => { t.plan(6) - _flatOptions.token = '@foo/' + flatOptions.token = '@foo/' npmlog.verbose = (title, msg) => { t.equal(title, 'logout', 'should have correcct log prefix') @@ -39,7 +41,7 @@ test('token logout', async (t) => { ) } - config.clearCredentialsByURI = (registry) => { + npm.config.clearCredentialsByURI = (registry) => { t.equal( registry, 'https://registry.npmjs.org/', @@ -47,7 +49,7 @@ test('token logout', async (t) => { ) } - config.save = (type) => { + npm.config.save = (type) => { t.equal(type, 'user', 'should save to user config') } @@ -70,7 +72,7 @@ test('token logout', async (t) => { 'should call npm-registry-fetch with expected values' ) - delete _flatOptions.token + delete flatOptions.token result = null mocks['npm-registry-fetch'] = null config.clearCredentialsByURI = null @@ -86,9 +88,11 @@ test('token logout', async (t) => { test('token scoped logout', async (t) => { t.plan(8) - _flatOptions.token = '@foo/' - _flatOptions.scope = '@myscope' - _flatOptions['@myscope:registry'] = 'https://diff-registry.npmjs.com/' + flatOptions.token = '@foo/' + config.scope = '@myscope' + config['@myscope:registry'] = 'https://diff-registry.npmjs.com/' + flatOptions.scope = '@myscope' + flatOptions['@myscope:registry'] = 'https://diff-registry.npmjs.com/' npmlog.verbose = (title, msg) => { t.equal(title, 'logout', 'should have correcct log prefix') @@ -99,7 +103,7 @@ test('token scoped logout', async (t) => { ) } - config.clearCredentialsByURI = (registry) => { + npm.config.clearCredentialsByURI = (registry) => { t.equal( registry, 'https://diff-registry.npmjs.com/', @@ -107,7 +111,7 @@ test('token scoped logout', async (t) => { ) } - config.delete = (ref, type) => { + npm.config.delete = (ref, type) => { t.equal( ref, '@myscope:registry', @@ -116,7 +120,7 @@ test('token scoped logout', async (t) => { t.equal(type, 'user', 'should delete from user config') } - config.save = (type) => { + npm.config.save = (type) => { t.equal(type, 'user', 'should save to user config') } @@ -140,9 +144,9 @@ test('token scoped logout', async (t) => { 'should call npm-registry-fetch with expected values' ) - _flatOptions.scope = '' - delete _flatOptions['@myscope:registry'] - delete _flatOptions.token + config.scope = '' + delete config['@myscope:registry'] + delete flatOptions.token result = null mocks['npm-registry-fetch'] = null config.clearCredentialsByURI = null @@ -158,8 +162,8 @@ test('token scoped logout', async (t) => { test('user/pass logout', async (t) => { t.plan(3) - _flatOptions.username = 'foo' - _flatOptions.password = 'bar' + flatOptions.username = 'foo' + flatOptions.password = 'bar' npmlog.verbose = (title, msg) => { t.equal(title, 'logout', 'should have correcct log prefix') @@ -170,17 +174,17 @@ test('user/pass logout', async (t) => { ) } - config.clearCredentialsByURI = () => null - config.save = () => null + npm.config.clearCredentialsByURI = () => null + npm.config.save = () => null await new Promise((res, rej) => { logout.exec([], (err) => { t.ifError(err, 'should not error out') - delete _flatOptions.username - delete _flatOptions.password - config.clearCredentialsByURI = null - config.save = null + delete flatOptions.username + delete flatOptions.password + npm.config.clearCredentialsByURI = null + npm.config.save = null npmlog.verbose = null res() @@ -203,9 +207,9 @@ test('missing credentials', (t) => { test('ignore invalid scoped registry config', async (t) => { t.plan(5) - _flatOptions.token = '@foo/' - _flatOptions.scope = '@myscope' - _flatOptions['@myscope:registry'] = '' + flatOptions.token = '@foo/' + config.scope = '@myscope' + flatOptions['@myscope:registry'] = '' npmlog.verbose = (title, msg) => { t.equal(title, 'logout', 'should have correcct log prefix') @@ -216,7 +220,7 @@ test('ignore invalid scoped registry config', async (t) => { ) } - config.clearCredentialsByURI = (registry) => { + npm.config.clearCredentialsByURI = (registry) => { t.equal( registry, 'https://registry.npmjs.org/', @@ -224,8 +228,8 @@ test('ignore invalid scoped registry config', async (t) => { ) } - config.delete = () => null - config.save = () => null + npm.config.delete = () => null + npm.config.save = () => null await new Promise((res, rej) => { logout.exec([], (err) => { @@ -247,7 +251,7 @@ test('ignore invalid scoped registry config', async (t) => { 'should call npm-registry-fetch with expected values' ) - delete _flatOptions.token + delete flatOptions.token result = null mocks['npm-registry-fetch'] = null config.clearCredentialsByURI = null diff --git a/deps/npm/test/lib/ls.js b/deps/npm/test/lib/ls.js index bcbd341356..5367dec688 100644 --- a/deps/npm/test/lib/ls.js +++ b/deps/npm/test/lib/ls.js @@ -1,4 +1,5 @@ const t = require('tap') +const mockNpm = require('../fixtures/mock-npm') const { resolve } = require('path') const { utimesSync } = require('fs') @@ -84,12 +85,9 @@ const diffDepTypesNmFixture = { }, } -let prefix -let globalDir = 'MISSING_GLOBAL_DIR' let result = '' -// note this _flatOptions representations is for tests-only and does not -// represent exactly the properties found in the actual flatOptions obj -const _flatOptions = { +const LS = require('../../lib/ls.js') +const config = { all: true, color: false, dev: false, @@ -99,32 +97,18 @@ const _flatOptions = { link: false, only: null, parseable: false, - get prefix () { - return prefix - }, production: false, } -const LS = require('../../lib/ls.js') -const ls = new LS({ - flatOptions: _flatOptions, - limit: { - fetch: 3, - }, - get prefix () { - return _flatOptions.prefix - }, - get globalDir () { - return globalDir - }, - config: { - get (key) { - return _flatOptions[key] - }, - }, +const flatOptions = { +} +const npm = mockNpm({ + config, + flatOptions, output: msg => { result = msg }, }) +const ls = new LS(npm) const redactCwd = res => res && res.replace(/\\+/g, '/').replace(new RegExp(__dirname.replace(/\\+/g, '/'), 'gi'), '{CWD}') @@ -138,10 +122,10 @@ const cleanUpResult = (done, t) => { t.test('ls', (t) => { t.beforeEach(cleanUpResult) - _flatOptions.json = false - _flatOptions.unicode = false + config.json = false + config.unicode = false t.test('no args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -160,7 +144,7 @@ t.test('ls', (t) => { }) t.test('missing package.json', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ ...simpleNmFixture, }) ls.exec([], (err) => { @@ -175,7 +159,7 @@ t.test('ls', (t) => { }) t.test('extraneous deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -198,8 +182,8 @@ t.test('ls', (t) => { }) t.test('with filter arg', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -213,15 +197,15 @@ t.test('ls', (t) => { ls.exec(['lorem'], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered by package and colored output') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('with dot filter arg', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -235,14 +219,14 @@ t.test('ls', (t) => { ls.exec(['.'], (err) => { t.ifError(err, 'should not throw on missing dep above current level') t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered by package and colored output') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('with filter arg nested dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -261,7 +245,7 @@ t.test('ls', (t) => { }) t.test('with multiple filter args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -289,7 +273,7 @@ t.test('ls', (t) => { }) t.test('with missing filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -314,9 +298,9 @@ t.test('ls', (t) => { }) t.test('default --depth value should be 0', (t) => { - _flatOptions.all = false - _flatOptions.depth = undefined - prefix = t.testdir({ + config.all = false + config.depth = undefined + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -330,16 +314,16 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing only top-level dependencies') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -353,16 +337,16 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing only top-level dependencies') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=1', (t) => { - _flatOptions.all = false - _flatOptions.depth = 1 - prefix = t.testdir({ + config.all = false + config.depth = 1 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -414,14 +398,14 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing top-level deps and their deps only') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('missing/invalid/extraneous', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -447,8 +431,8 @@ t.test('ls', (t) => { }) t.test('colored output', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -462,14 +446,14 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.equal(err.code, 'ELSPROBLEMS', 'should have error code') t.matchSnapshot(redactCwd(result), 'should output tree containing color info') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('--dev', (t) => { - _flatOptions.dev = true - prefix = t.testdir({ + config.dev = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -491,14 +475,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing dev deps') - _flatOptions.dev = false + config.dev = false t.end() }) }) t.test('--only=development', (t) => { - _flatOptions.only = 'development' - prefix = t.testdir({ + config.only = 'development' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -520,14 +504,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing only development deps') - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--link', (t) => { - _flatOptions.link = true - prefix = t.testdir({ + config.link = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -559,13 +543,13 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing linked deps') - _flatOptions.link = false + config.link = false t.end() }) }) t.test('print deduped symlinks', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'print-deduped-symlinks', version: '1.0.0', @@ -595,14 +579,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing linked deps') - _flatOptions.link = false + config.link = false t.end() }) }) t.test('--production', (t) => { - _flatOptions.production = true - prefix = t.testdir({ + config.production = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -624,14 +608,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing production deps') - _flatOptions.production = false + config.production = false t.end() }) }) t.test('--only=prod', (t) => { - _flatOptions.only = 'prod' - prefix = t.testdir({ + config.only = 'prod' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -653,14 +637,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing only prod deps') - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--long', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -682,16 +666,16 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree info with descriptions') - _flatOptions.long = true + config.long = true t.end() }) }) t.test('--long --depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - _flatOptions.long = true - prefix = t.testdir({ + config.all = false + config.depth = 0 + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -713,15 +697,15 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing top-level deps with descriptions') - _flatOptions.all = true - _flatOptions.depth = Infinity - _flatOptions.long = false + config.all = true + config.depth = Infinity + config.long = false t.end() }) }) t.test('json read problems', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': '{broken json', }) ls.exec([], (err) => { @@ -732,7 +716,7 @@ t.test('ls', (t) => { }) t.test('empty location', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) ls.exec([], (err) => { t.ifError(err, 'should not error out on empty locations') t.matchSnapshot(redactCwd(result), 'should print empty result') @@ -741,7 +725,7 @@ t.test('ls', (t) => { }) t.test('invalid peer dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -768,8 +752,8 @@ t.test('ls', (t) => { }) t.test('invalid deduped dep', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'invalid-deduped-dep', version: '1.0.0', @@ -798,13 +782,13 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree signaling mismatching peer dep in problems') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('deduped missing dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -834,7 +818,7 @@ t.test('ls', (t) => { }) t.test('unmet peer dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -852,8 +836,8 @@ t.test('ls', (t) => { }) t.test('unmet optional dep', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -878,13 +862,13 @@ t.test('ls', (t) => { t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code') t.match(err.message, /invalid: optional-dep@1.0.0/, 'should have invalid dep error msg') t.matchSnapshot(redactCwd(result), 'should output tree with empty entry for missing optional deps') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('cycle deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -921,8 +905,8 @@ t.test('ls', (t) => { }) t.test('cycle deps with filter args', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -954,13 +938,13 @@ t.test('ls', (t) => { ls.exec(['a'], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('with no args dedupe entries', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'dedupe-entries', version: '1.0.0', @@ -1007,9 +991,9 @@ t.test('ls', (t) => { }) t.test('with no args dedupe entries and not displaying all', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'dedupe-entries', version: '1.0.0', @@ -1051,15 +1035,15 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('with args and dedupe entries', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'dedupe-entries', version: '1.0.0', @@ -1101,13 +1085,13 @@ t.test('ls', (t) => { ls.exec(['@npmcli/b'], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('with args and different order of items', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'dedupe-entries', version: '1.0.0', @@ -1154,7 +1138,7 @@ t.test('ls', (t) => { }) t.test('using aliases', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1189,7 +1173,7 @@ t.test('ls', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing aliases') t.end() @@ -1197,7 +1181,7 @@ t.test('ls', (t) => { }) t.test('resolved points to git ref', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1235,7 +1219,7 @@ t.test('ls', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing git refs') @@ -1244,7 +1228,7 @@ t.test('ls', (t) => { }) t.test('broken resolved field', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ node_modules: { a: { 'package.json': JSON.stringify({ @@ -1288,7 +1272,7 @@ t.test('ls', (t) => { }) t.test('from and resolved properties', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1334,7 +1318,7 @@ t.test('ls', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should not be printed in tree output') t.end() @@ -1342,7 +1326,7 @@ t.test('ls', (t) => { }) t.test('global', (t) => { - _flatOptions.global = true + config.global = true const fixtures = t.testdir({ node_modules: { a: { @@ -1369,18 +1353,18 @@ t.test('ls', (t) => { }) // mimics lib/npm.js globalDir getter but pointing to fixtures - globalDir = resolve(fixtures, 'node_modules') + npm.globalDir = resolve(fixtures, 'node_modules') ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should print tree and not mark top-level items extraneous') - globalDir = 'MISSING_GLOBAL_DIR' - _flatOptions.global = false + npm.globalDir = 'MISSING_GLOBAL_DIR' + config.global = false t.end() }) }) t.test('filtering by child of missing dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'filter-by-child-of-missing-dep', version: '1.0.0', @@ -1432,7 +1416,7 @@ t.test('ls', (t) => { }) t.test('loading a tree containing workspaces', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'filter-by-child-of-missing-dep', version: '1.0.0', @@ -1483,8 +1467,8 @@ t.test('ls', (t) => { }) t.test('filter pkg arg using depth option', (t) => { - _flatOptions.depth = 0 - prefix = t.testdir({ + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-pkg-arg-filter-with-depth-opt', version: '1.0.0', @@ -1540,7 +1524,7 @@ t.test('ls', (t) => { t.matchSnapshot(redactCwd(result), 'should print empty results msg') // if no --depth config is defined, should print path to dep - _flatOptions.depth = null // default config value + config.depth = null // default config value ls.exec(['d'], (err) => { t.ifError(err, 'should NOT have ELSPROBLEMS error code when filter') t.matchSnapshot(redactCwd(result), 'should print expected result') @@ -1550,7 +1534,7 @@ t.test('ls', (t) => { }) t.teardown(() => { - _flatOptions.depth = Infinity + config.depth = Infinity }) t.end() @@ -1558,11 +1542,11 @@ t.test('ls', (t) => { t.test('ls --parseable', (t) => { t.beforeEach(cleanUpResult) - _flatOptions.json = false - _flatOptions.unicode = false - _flatOptions.parseable = true + config.json = false + config.unicode = false + config.parseable = true t.test('no args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1581,7 +1565,7 @@ t.test('ls --parseable', (t) => { }) t.test('missing package.json', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ ...simpleNmFixture, }) ls.exec([], (err) => { @@ -1596,7 +1580,7 @@ t.test('ls --parseable', (t) => { }) t.test('extraneous deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1614,7 +1598,7 @@ t.test('ls --parseable', (t) => { }) t.test('with filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1633,7 +1617,7 @@ t.test('ls --parseable', (t) => { }) t.test('with filter arg nested dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1652,7 +1636,7 @@ t.test('ls --parseable', (t) => { }) t.test('with multiple filter args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1680,7 +1664,7 @@ t.test('ls --parseable', (t) => { }) t.test('with missing filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1705,9 +1689,9 @@ t.test('ls --parseable', (t) => { }) t.test('default --depth value should be 0', (t) => { - _flatOptions.all = false - _flatOptions.depth = undefined - prefix = t.testdir({ + config.all = false + config.depth = undefined + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1721,16 +1705,16 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output parseable output containing only top-level dependencies') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1744,16 +1728,16 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing only top-level dependencies') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=1', (t) => { - _flatOptions.all = false - _flatOptions.depth = 1 - prefix = t.testdir({ + config.all = false + config.depth = 1 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1767,14 +1751,14 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output parseable containing top-level deps and their deps only') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('missing/invalid/extraneous', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1793,8 +1777,8 @@ t.test('ls --parseable', (t) => { }) t.test('--dev', (t) => { - _flatOptions.dev = true - prefix = t.testdir({ + config.dev = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1816,14 +1800,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing dev deps') - _flatOptions.dev = false + config.dev = false t.end() }) }) t.test('--only=development', (t) => { - _flatOptions.only = 'development' - prefix = t.testdir({ + config.only = 'development' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1845,14 +1829,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing only development deps') - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--link', (t) => { - _flatOptions.link = true - prefix = t.testdir({ + config.link = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1884,14 +1868,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing linked deps') - _flatOptions.link = false + config.link = false t.end() }) }) t.test('--production', (t) => { - _flatOptions.production = true - prefix = t.testdir({ + config.production = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1913,14 +1897,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing production deps') - _flatOptions.production = false + config.production = false t.end() }) }) t.test('--only=prod', (t) => { - _flatOptions.only = 'prod' - prefix = t.testdir({ + config.only = 'prod' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1942,14 +1926,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing only prod deps') - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--long', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1971,13 +1955,13 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree info with descriptions') - _flatOptions.long = true + config.long = true t.end() }) }) t.test('--long with extraneous deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1996,8 +1980,8 @@ t.test('ls --parseable', (t) => { }) t.test('--long missing/invalid/extraneous', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2011,14 +1995,14 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.match(err, { code: 'ELSPROBLEMS' }, 'should list dep problems') t.matchSnapshot(redactCwd(result), 'should output parseable result containing EXTRANEOUS/INVALID labels') - _flatOptions.long = false + config.long = false t.end() }) }) t.test('--long print symlink target location', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2051,16 +2035,16 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output parseable results with symlink targets') - _flatOptions.long = false + config.long = false t.end() }) }) t.test('--long --depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - _flatOptions.long = true - prefix = t.testdir({ + config.all = false + config.depth = 0 + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2082,15 +2066,15 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing top-level deps with descriptions') - _flatOptions.all = true - _flatOptions.depth = Infinity - _flatOptions.long = false + config.all = true + config.depth = Infinity + config.long = false t.end() }) }) t.test('json read problems', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': '{broken json', }) ls.exec([], (err) => { @@ -2101,7 +2085,7 @@ t.test('ls --parseable', (t) => { }) t.test('empty location', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) ls.exec([], (err) => { t.ifError(err, 'should not error out on empty locations') t.matchSnapshot(redactCwd(result), 'should print empty result') @@ -2110,7 +2094,7 @@ t.test('ls --parseable', (t) => { }) t.test('unmet peer dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2137,7 +2121,7 @@ t.test('ls --parseable', (t) => { }) t.test('unmet optional dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2167,7 +2151,7 @@ t.test('ls --parseable', (t) => { }) t.test('cycle deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2203,7 +2187,7 @@ t.test('ls --parseable', (t) => { }) t.test('using aliases', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2234,7 +2218,7 @@ t.test('ls --parseable', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing aliases') t.end() @@ -2242,7 +2226,7 @@ t.test('ls --parseable', (t) => { }) t.test('resolved points to git ref', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2279,7 +2263,7 @@ t.test('ls --parseable', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing git refs') t.end() @@ -2287,7 +2271,7 @@ t.test('ls --parseable', (t) => { }) t.test('from and resolved properties', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2333,7 +2317,7 @@ t.test('ls --parseable', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should not be printed in tree output') t.end() @@ -2341,7 +2325,7 @@ t.test('ls --parseable', (t) => { }) t.test('global', (t) => { - _flatOptions.global = true + config.global = true const fixtures = t.testdir({ node_modules: { a: { @@ -2368,12 +2352,12 @@ t.test('ls --parseable', (t) => { }) // mimics lib/npm.js globalDir getter but pointing to fixtures - globalDir = resolve(fixtures, 'node_modules') + npm.globalDir = resolve(fixtures, 'node_modules') ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should print parseable output for global deps') - globalDir = 'MISSING_GLOBAL_DIR' - _flatOptions.global = false + npm.globalDir = 'MISSING_GLOBAL_DIR' + config.global = false t.end() }) }) @@ -2383,10 +2367,10 @@ t.test('ls --parseable', (t) => { t.test('ls --json', (t) => { t.beforeEach(cleanUpResult) - _flatOptions.json = true - _flatOptions.parseable = false + config.json = true + config.parseable = false t.test('no args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2425,7 +2409,7 @@ t.test('ls --json', (t) => { }) t.test('missing package.json', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ ...simpleNmFixture, }) ls.exec([], (err) => { @@ -2474,7 +2458,7 @@ t.test('ls --json', (t) => { }) t.test('extraneous deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2528,7 +2512,7 @@ t.test('ls --json', (t) => { }) t.test('with filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2564,7 +2548,7 @@ t.test('ls --json', (t) => { }) t.test('with filter arg nested dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2600,7 +2584,7 @@ t.test('ls --json', (t) => { }) t.test('with multiple filter args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2648,7 +2632,7 @@ t.test('ls --json', (t) => { }) t.test('with missing filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2680,9 +2664,9 @@ t.test('ls --json', (t) => { }) t.test('default --depth value should now be 0', (t) => { - _flatOptions.all = false - _flatOptions.depth = undefined - prefix = t.testdir({ + config.all = false + config.depth = undefined + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2711,16 +2695,16 @@ t.test('ls --json', (t) => { }, 'should output json containing only top-level dependencies' ) - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2749,16 +2733,16 @@ t.test('ls --json', (t) => { }, 'should output json containing only top-level dependencies' ) - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=1', (t) => { - _flatOptions.all = false - _flatOptions.depth = 1 - prefix = t.testdir({ + config.all = false + config.depth = 1 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2792,14 +2776,14 @@ t.test('ls --json', (t) => { }, 'should output json containing top-level deps and their deps only' ) - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('missing/invalid/extraneous', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2858,8 +2842,8 @@ t.test('ls --json', (t) => { }) t.test('--dev', (t) => { - _flatOptions.dev = true - prefix = t.testdir({ + config.dev = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2899,14 +2883,14 @@ t.test('ls --json', (t) => { }, 'should output json containing dev deps' ) - _flatOptions.dev = false + config.dev = false t.end() }) }) t.test('--only=development', (t) => { - _flatOptions.only = 'development' - prefix = t.testdir({ + config.only = 'development' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2946,14 +2930,14 @@ t.test('ls --json', (t) => { }, 'should output json containing only development deps' ) - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--link', (t) => { - _flatOptions.link = true - prefix = t.testdir({ + config.link = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2998,14 +2982,14 @@ t.test('ls --json', (t) => { }, 'should output json containing linked deps' ) - _flatOptions.link = false + config.link = false t.end() }) }) t.test('--production', (t) => { - _flatOptions.production = true - prefix = t.testdir({ + config.production = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3039,14 +3023,14 @@ t.test('ls --json', (t) => { }, 'should output json containing production deps' ) - _flatOptions.production = false + config.production = false t.end() }) }) t.test('--only=prod', (t) => { - _flatOptions.only = 'prod' - prefix = t.testdir({ + config.only = 'prod' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3080,13 +3064,13 @@ t.test('ls --json', (t) => { }, 'should output json containing only prod deps' ) - _flatOptions.only = null + config.only = null t.end() }) }) t.test('from lockfile', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ node_modules: { '@isaacs': { 'dedupe-tests-a': { @@ -3215,8 +3199,8 @@ t.test('ls --json', (t) => { }) t.test('--long', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3345,16 +3329,16 @@ t.test('ls --json', (t) => { }, 'should output long json info' ) - _flatOptions.long = true + config.long = true t.end() }) }) t.test('--long --depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - _flatOptions.long = true - prefix = t.testdir({ + config.all = false + config.depth = 0 + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3446,15 +3430,15 @@ t.test('ls --json', (t) => { }, 'should output json containing top-level deps in long format' ) - _flatOptions.all = true - _flatOptions.depth = Infinity - _flatOptions.long = false + config.all = true + config.depth = Infinity + config.long = false t.end() }) }) t.test('json read problems', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': '{broken json', }) ls.exec([], (err) => { @@ -3475,7 +3459,7 @@ t.test('ls --json', (t) => { }) t.test('empty location', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) ls.exec([], (err) => { t.ifError(err, 'should not error out on empty locations') t.deepEqual( @@ -3488,7 +3472,7 @@ t.test('ls --json', (t) => { }) t.test('unmet peer dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3547,7 +3531,7 @@ t.test('ls --json', (t) => { }) t.test('unmet optional dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3611,7 +3595,7 @@ t.test('ls --json', (t) => { }) t.test('cycle deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3667,7 +3651,7 @@ t.test('ls --json', (t) => { }) t.test('using aliases', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3697,7 +3681,7 @@ t.test('ls --json', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.deepEqual( jsonParse(result), @@ -3718,7 +3702,7 @@ t.test('ls --json', (t) => { }) t.test('resolved points to git ref', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3757,7 +3741,7 @@ t.test('ls --json', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.deepEqual( jsonParse(result), @@ -3778,7 +3762,7 @@ t.test('ls --json', (t) => { }) t.test('from and resolved properties', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3841,7 +3825,7 @@ t.test('ls --json', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.deepEqual( jsonParse(result), @@ -3862,7 +3846,7 @@ t.test('ls --json', (t) => { }) t.test('node.name fallback if missing root package name', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ version: '1.0.0', }), @@ -3881,7 +3865,7 @@ t.test('ls --json', (t) => { }) t.test('global', (t) => { - _flatOptions.global = true + config.global = true const fixtures = t.testdir({ node_modules: { a: { @@ -3908,7 +3892,7 @@ t.test('ls --json', (t) => { }) // mimics lib/npm.js globalDir getter but pointing to fixtures - globalDir = resolve(fixtures, 'node_modules') + npm.globalDir = resolve(fixtures, 'node_modules') ls.exec([], () => { t.deepEqual( @@ -3931,8 +3915,8 @@ t.test('ls --json', (t) => { }, 'should print json output for global deps' ) - globalDir = 'MISSING_GLOBAL_DIR' - _flatOptions.global = false + npm.globalDir = 'MISSING_GLOBAL_DIR' + config.global = false t.end() }) }) diff --git a/deps/npm/test/lib/npm.js b/deps/npm/test/lib/npm.js index 87cbea8f2c..5739193980 100644 --- a/deps/npm/test/lib/npm.js +++ b/deps/npm/test/lib/npm.js @@ -42,7 +42,7 @@ const npmlog = require('npmlog') const npmPath = resolve(__dirname, '..', '..') const Config = require('@npmcli/config') -const { types, defaults, shorthands } = require('../../lib/utils/config.js') +const { definitions, shorthands, flatten } = require('../../lib/utils/config') const freshConfig = (opts = {}) => { for (const env of Object.keys(process.env).filter(e => /^npm_/.test(e))) delete process.env[env] @@ -50,12 +50,12 @@ const freshConfig = (opts = {}) => { process.env.npm_config_cache = CACHE npm.config = new Config({ - types, - defaults, + definitions, shorthands, npmPath, log: npmlog, ...opts, + flatten, }) } @@ -145,6 +145,7 @@ t.test('npm.load', t => { t.equal(npm.loading, false, 'not loading yet') const p = npm.load(first).then(() => { + t.ok(npm.usage, 'has usage') npm.config.set('prefix', dir) t.match(npm, { loaded: true, @@ -160,7 +161,7 @@ t.test('npm.load', t => { npm.load(third) t.equal(thirdCalled, true, 'third callbback got called') t.match(logs, [ - ['timing', 'npm:load', /Completed in [0-9]+ms/], + ['timing', 'npm:load', /Completed in [0-9.]+ms/], ]) logs.length = 0 @@ -289,6 +290,11 @@ t.test('npm.load', t => { t.equal(npm.config.get('scope'), '@foo', 'added the @ sign to scope') t.match(logs.filter(l => l[0] !== 'timing' || !/^config:/.test(l[1])), [ [ + 'timing', + 'npm:load:whichnode', + /Completed in [0-9.]+ms/, + ], + [ 'verbose', 'node symlink', resolve(dir, 'bin', node), @@ -296,7 +302,7 @@ t.test('npm.load', t => { [ 'timing', 'npm:load', - /Completed in [0-9]+ms/, + /Completed in [0-9.]+ms/, ], ]) logs.length = 0 @@ -307,6 +313,9 @@ t.test('npm.load', t => { if (er) throw er + t.equal(npm.command, 'll', 'command set to first npm command') + t.equal(npm.flatOptions.npmCommand, 'll', 'npmCommand flatOption set') + t.same(consoleLogs, [[npm.commands.ll.usage]], 'print usage') consoleLogs.length = 0 npm.config.set('usage', false) @@ -318,6 +327,9 @@ t.test('npm.load', t => { if (er) throw er + t.strictSame([npm.command, npm.flatOptions.npmCommand], ['ll', 'll'], + 'does not change npm.command when another command is called') + t.match(logs, [ [ 'error', @@ -328,12 +340,12 @@ t.test('npm.load', t => { [ 'timing', 'command:config', - /Completed in [0-9]+ms/, + /Completed in [0-9.]+ms/, ], [ 'timing', 'command:get', - /Completed in [0-9]+ms/, + /Completed in [0-9.]+ms/, ], ]) t.same(consoleLogs, [['scope=@foo\n\u2010not-a-dash=undefined']]) @@ -343,6 +355,84 @@ t.test('npm.load', t => { await new Promise((res) => setTimeout(res)) }) + t.test('workpaces-aware configs and commands', async t => { + const dir = t.testdir({ + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + scripts: { test: 'echo test a' }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '1.0.0', + scripts: { test: 'echo test b' }, + }), + }, + }, + 'package.json': JSON.stringify({ + name: 'root', + version: '1.0.0', + workspaces: ['./packages/*'], + }), + '.npmrc': '', + }) + + const { log } = console + const consoleLogs = [] + console.log = (...msg) => consoleLogs.push(msg) + + const { execPath } = process + t.teardown(() => { + console.log = log + }) + + freshConfig({ + argv: [ + execPath, + process.argv[1], + '--userconfig', + resolve(dir, '.npmrc'), + '--color', + 'false', + '--workspaces', + 'true', + ], + }) + + await npm.load(er => { + if (er) + throw er + }) + + npm.localPrefix = dir + + await new Promise((res, rej) => { + npm.commands['run-script']([], er => { + if (er) + rej(er) + + t.match( + consoleLogs, + [ + ['Lifecycle scripts included in a@1.0.0:'], + [' test\n echo test a'], + [''], + ['Lifecycle scripts included in b@1.0.0:'], + [' test\n echo test b'], + [''], + ], + 'should exec workspaces version of commands' + ) + + res() + }) + }) + }) + t.end() }) diff --git a/deps/npm/test/lib/outdated.js b/deps/npm/test/lib/outdated.js index 02952971b6..5aff7c37ac 100644 --- a/deps/npm/test/lib/outdated.js +++ b/deps/npm/test/lib/outdated.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const packument = spec => { const mocks = { @@ -89,12 +90,13 @@ const outdated = (dir, opts) => { packument, }, }) - return new Outdated({ + const npm = mockNpm({ + ...opts, prefix: dir, globalDir: `${globalDir}/node_modules`, - flatOptions: opts, output, }) + return new Outdated(npm) } t.beforeEach((done) => { @@ -173,7 +175,7 @@ t.test('should display outdated deps', t => { t.test('outdated global', t => { outdated(null, { - global: true, + config: { global: true }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -182,7 +184,9 @@ t.test('should display outdated deps', t => { t.test('outdated', t => { outdated(testDir, { - global: false, + config: { + global: false, + }, color: true, }).exec([], () => { t.matchSnapshot(logs) @@ -192,9 +196,11 @@ t.test('should display outdated deps', t => { t.test('outdated --omit=dev', t => { outdated(testDir, { - global: false, + config: { + global: false, + omit: ['dev'], + }, color: true, - omit: ['dev'], }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -203,9 +209,11 @@ t.test('should display outdated deps', t => { t.test('outdated --omit=dev --omit=peer', t => { outdated(testDir, { - global: false, + config: { + global: false, + omit: ['dev', 'peer'], + }, color: true, - omit: ['dev', 'peer'], }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -214,9 +222,11 @@ t.test('should display outdated deps', t => { t.test('outdated --omit=prod', t => { outdated(testDir, { - global: false, + config: { + global: false, + omit: ['prod'], + }, color: true, - omit: ['prod'], }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -225,8 +235,10 @@ t.test('should display outdated deps', t => { t.test('outdated --long', t => { outdated(testDir, { - global: false, - long: true, + config: { + global: false, + long: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -235,8 +247,10 @@ t.test('should display outdated deps', t => { t.test('outdated --json', t => { outdated(testDir, { - global: false, - json: true, + config: { + global: false, + json: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -245,9 +259,11 @@ t.test('should display outdated deps', t => { t.test('outdated --json --long', t => { outdated(testDir, { - global: false, - json: true, - long: true, + config: { + global: false, + json: true, + long: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -256,8 +272,10 @@ t.test('should display outdated deps', t => { t.test('outdated --parseable', t => { outdated(testDir, { - global: false, - parseable: true, + config: { + global: false, + parseable: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -266,9 +284,11 @@ t.test('should display outdated deps', t => { t.test('outdated --parseable --long', t => { outdated(testDir, { - global: false, - parseable: true, - long: true, + config: { + global: false, + parseable: true, + long: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -277,7 +297,9 @@ t.test('should display outdated deps', t => { t.test('outdated --all', t => { outdated(testDir, { - all: true, + config: { + all: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -286,7 +308,9 @@ t.test('should display outdated deps', t => { t.test('outdated specific dep', t => { outdated(testDir, { - global: false, + config: { + global: false, + }, }).exec(['alpha'], () => { t.matchSnapshot(logs) t.end() diff --git a/deps/npm/test/lib/pack.js b/deps/npm/test/lib/pack.js index ce319be76b..a5163acfdc 100644 --- a/deps/npm/test/lib/pack.js +++ b/deps/npm/test/lib/pack.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const OUTPUT = [] const output = (...msg) => OUTPUT.push(msg) @@ -25,14 +26,15 @@ t.test('should pack current directory with no arguments', (t) => { clearProgress: () => {}, }, }) - const pack = new Pack({ - flatOptions: { + const npm = mockNpm({ + config: { unicode: false, json: false, - dryRun: false, + 'dry-run': false, }, output, }) + const pack = new Pack(npm) pack.exec([], er => { if (er) @@ -60,14 +62,15 @@ t.test('should pack given directory', (t) => { clearProgress: () => {}, }, }) - const pack = new Pack({ - flatOptions: { + const npm = mockNpm({ + config: { unicode: true, json: true, - dryRun: true, + 'dry-run': true, }, output, }) + const pack = new Pack(npm) pack.exec([testDir], er => { if (er) @@ -95,14 +98,15 @@ t.test('should pack given directory for scoped package', (t) => { clearProgress: () => {}, }, }) - const pack = new Pack({ - flatOptions: { + const npm = mockNpm({ + config: { unicode: true, json: true, - dryRun: true, + 'dry-run': true, }, output, }) + const pack = new Pack(npm) return pack.exec([testDir], er => { if (er) @@ -129,14 +133,15 @@ t.test('should log pack contents', (t) => { clearProgress: () => {}, }, }) - const pack = new Pack({ - flatOptions: { + const npm = mockNpm({ + config: { unicode: false, json: false, - dryRun: false, + 'dry-run': false, }, output, }) + const pack = new Pack(npm) pack.exec([], er => { if (er) diff --git a/deps/npm/test/lib/ping.js b/deps/npm/test/lib/ping.js index f3563036f7..95361035ac 100644 --- a/deps/npm/test/lib/ping.js +++ b/deps/npm/test/lib/ping.js @@ -1,14 +1,15 @@ const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('pings', (t) => { t.plan(8) - const flatOptions = { registry: 'https://registry.npmjs.org' } + const registry = 'https://registry.npmjs.org' let noticeCalls = 0 const Ping = requireInject('../../lib/ping.js', { '../../lib/utils/ping.js': function (spec) { - t.equal(spec, flatOptions, 'passes flatOptions') + t.equal(spec.registry, registry, 'passes flatOptions') return {} }, npmlog: { @@ -16,7 +17,7 @@ test('pings', (t) => { ++noticeCalls if (noticeCalls === 1) { t.equal(type, 'PING', 'should log a PING') - t.equal(spec, flatOptions.registry, 'should log the registry url') + t.equal(spec, registry, 'should log the registry url') } else { t.equal(type, 'PONG', 'should log a PONG') t.match(spec, /\d+ms/, 'should log the elapsed milliseconds') @@ -24,7 +25,11 @@ test('pings', (t) => { }, }, }) - const ping = new Ping({ flatOptions }) + const npm = mockNpm({ + config: { registry }, + flatOptions: { registry }, + }) + const ping = new Ping(npm) ping.exec([], (err) => { t.equal(noticeCalls, 2, 'should have logged 2 lines') @@ -36,12 +41,12 @@ test('pings', (t) => { test('pings and logs details', (t) => { t.plan(10) - const flatOptions = { registry: 'https://registry.npmjs.org' } + const registry = 'https://registry.npmjs.org' const details = { extra: 'data' } let noticeCalls = 0 const Ping = requireInject('../../lib/ping.js', { '../../lib/utils/ping.js': function (spec) { - t.equal(spec, flatOptions, 'passes flatOptions') + t.equal(spec.registry, registry, 'passes flatOptions') return details }, npmlog: { @@ -49,7 +54,7 @@ test('pings and logs details', (t) => { ++noticeCalls if (noticeCalls === 1) { t.equal(type, 'PING', 'should log a PING') - t.equal(spec, flatOptions.registry, 'should log the registry url') + t.equal(spec, registry, 'should log the registry url') } else if (noticeCalls === 2) { t.equal(type, 'PONG', 'should log a PONG') t.match(spec, /\d+ms/, 'should log the elapsed milliseconds') @@ -61,7 +66,11 @@ test('pings and logs details', (t) => { }, }, }) - const ping = new Ping({ flatOptions }) + const npm = mockNpm({ + config: { registry }, + flatOptions: { registry }, + }) + const ping = new Ping(npm) ping.exec([], (err) => { t.equal(noticeCalls, 3, 'should have logged 3 lines') @@ -73,12 +82,12 @@ test('pings and logs details', (t) => { test('pings and returns json', (t) => { t.plan(11) - const flatOptions = { registry: 'https://registry.npmjs.org', json: true } + const registry = 'https://registry.npmjs.org' const details = { extra: 'data' } let noticeCalls = 0 const Ping = requireInject('../../lib/ping.js', { '../../lib/utils/ping.js': function (spec) { - t.equal(spec, flatOptions, 'passes flatOptions') + t.equal(spec.registry, registry, 'passes flatOptions') return details }, npmlog: { @@ -86,7 +95,7 @@ test('pings and returns json', (t) => { ++noticeCalls if (noticeCalls === 1) { t.equal(type, 'PING', 'should log a PING') - t.equal(spec, flatOptions.registry, 'should log the registry url') + t.equal(spec, registry, 'should log the registry url') } else { t.equal(type, 'PONG', 'should log a PONG') t.match(spec, /\d+ms/, 'should log the elapsed milliseconds') @@ -94,15 +103,17 @@ test('pings and returns json', (t) => { }, }, }) - const ping = new Ping({ - flatOptions, + const npm = mockNpm({ + config: { registry, json: true }, + flatOptions: { registry }, output: function (spec) { const parsed = JSON.parse(spec) - t.equal(parsed.registry, flatOptions.registry, 'returns the correct registry url') + t.equal(parsed.registry, registry, 'returns the correct registry url') t.match(parsed.details, details, 'prints returned details') t.type(parsed.time, 'number', 'returns time as a number') }, }) + const ping = new Ping(npm) ping.exec([], (err) => { t.equal(noticeCalls, 2, 'should have logged 2 lines') diff --git a/deps/npm/test/lib/profile.js b/deps/npm/test/lib/profile.js index d1be93b0cb..5b1210615a 100644 --- a/deps/npm/test/lib/profile.js +++ b/deps/npm/test/lib/profile.js @@ -1,20 +1,24 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let result = '' -const flatOptions = { +const config = { otp: '', json: false, parseable: false, registry: 'https://registry.npmjs.org/', } -const npm = { - config: {}, - flatOptions: { ...flatOptions }, +const flatOptions = { + registry: 'https://registry.npmjs.org/', +} +const npm = mockNpm({ + config, + flatOptions, output: (...msg) => { result = result ? `${result}\n${msg.join('\n')}` : msg.join('\n') }, -} +}) const mocks = { ansistyles: { bright: a => a }, npmlog: { @@ -65,8 +69,10 @@ const userProfile = { t.afterEach(cb => { result = '' - npm.config = {} - npm.flatOptions = { ...flatOptions } + flatOptions.otp = '' + config.json = false + config.parseable = false + config.registry = 'https://registry.npmjs.org/' cb() }) @@ -111,7 +117,7 @@ t.test('profile get no args', t => { }) t.test('--json', t => { - npm.flatOptions.json = true + config.json = true profile.exec(['get'], err => { if (err) @@ -127,7 +133,7 @@ t.test('profile get no args', t => { }) t.test('--parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true profile.exec(['get'], err => { if (err) @@ -256,7 +262,7 @@ t.test('profile get <key>', t => { }) t.test('--json', t => { - npm.flatOptions.json = true + config.json = true profile.exec(['get', 'name'], err => { if (err) @@ -272,7 +278,7 @@ t.test('profile get <key>', t => { }) t.test('--parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true profile.exec(['get', 'name'], err => { if (err) @@ -316,7 +322,7 @@ t.test('profile get multiple args', t => { }) t.test('--json', t => { - npm.flatOptions.json = true + config.json = true profile.exec(['get', 'name', 'email', 'github'], err => { if (err) @@ -332,7 +338,7 @@ t.test('profile get multiple args', t => { }) t.test('--parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true profile.exec(['get', 'name', 'email', 'github'], err => { if (err) @@ -451,7 +457,7 @@ t.test('profile set <key> <value>', t => { t.test('--json', t => { t.plan(2) - npm.flatOptions.json = true + config.json = true const Profile = requireInject('../../lib/profile.js', { ...mocks, @@ -476,7 +482,7 @@ t.test('profile set <key> <value>', t => { t.test('--parseable', t => { t.plan(2) - npm.flatOptions.parseable = true + config.parseable = true const Profile = requireInject('../../lib/profile.js', { ...mocks, @@ -705,7 +711,7 @@ t.test('enable-2fa', t => { }) t.test('no support for --json output', t => { - npm.flatOptions.json = true + config.json = true profile.exec(['enable-2fa', 'auth-only'], err => { t.match( @@ -719,7 +725,7 @@ t.test('enable-2fa', t => { }) t.test('no support for --parseable output', t => { - npm.flatOptions.parseable = true + config.parseable = true profile.exec(['enable-2fa', 'auth-only'], err => { t.match( @@ -817,18 +823,16 @@ t.test('enable-2fa', t => { t.plan(10) // mock legacy basic auth style - npm.config = { - getCredentialsByURI (reg) { - t.equal(reg, flatOptions.registry, 'should use expected registry') - return { auth: Buffer.from('foo:bar').toString('base64') } - }, - setCredentialsByURI (registry, { token }) { - t.equal(registry, flatOptions.registry, 'should set expected registry') - t.equal(token, 'token', 'should set expected token') - }, - save (type) { - t.equal(type, 'user', 'should save to user config') - }, + npm.config.getCredentialsByURI = (reg) => { + t.equal(reg, flatOptions.registry, 'should use expected registry') + return { auth: Buffer.from('foo:bar').toString('base64') } + } + npm.config.setCredentialsByURI = (registry, { token }) => { + t.equal(registry, flatOptions.registry, 'should set expected registry') + t.equal(token, 'token', 'should set expected token') + } + npm.config.save = (type) => { + t.equal(type, 'user', 'should save to user config') } const npmProfile = { @@ -901,12 +905,10 @@ t.test('enable-2fa', t => { t.test('from token and set otp, retries on pending and verifies with qrcode', t => { t.plan(4) - npm.flatOptions.otp = '1234' + flatOptions.otp = '1234' - npm.config = { - getCredentialsByURI () { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = () => { + return { token: 'token' } } let setCount = 0 @@ -1003,12 +1005,10 @@ t.test('enable-2fa', t => { }) t.test('from token and set otp, retrieves invalid otp', t => { - npm.flatOptions.otp = '1234' + flatOptions.otp = '1234' - npm.config = { - getCredentialsByURI () { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = () => { + return { token: 'token' } } const npmProfile = { @@ -1055,12 +1055,11 @@ t.test('enable-2fa', t => { }) t.test('from token auth provides --otp config arg', t => { - npm.flatOptions.otp = '123456' + flatOptions.otp = '123456' + flatOptions.otp = '123456' - npm.config = { - getCredentialsByURI (reg) { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = (reg) => { + return { token: 'token' } } const npmProfile = { @@ -1105,10 +1104,8 @@ t.test('enable-2fa', t => { }) t.test('missing tfa from user profile', t => { - npm.config = { - getCredentialsByURI (reg) { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = (reg) => { + return { token: 'token' } } const npmProfile = { @@ -1156,10 +1153,8 @@ t.test('enable-2fa', t => { }) t.test('defaults to auth-and-writes permission if no mode specified', t => { - npm.config = { - getCredentialsByURI (reg) { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = (reg) => { + return { token: 'token' } } const npmProfile = { @@ -1303,7 +1298,7 @@ t.test('disable-2fa', t => { }) t.test('--json', t => { - npm.flatOptions.json = true + config.json = true const Profile = requireInject('../../lib/profile.js', { ...mocks, @@ -1326,7 +1321,7 @@ t.test('disable-2fa', t => { }) t.test('--parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true const Profile = requireInject('../../lib/profile.js', { ...mocks, @@ -1354,7 +1349,7 @@ t.test('disable-2fa', t => { t.test('--otp config already set', t => { t.plan(3) - npm.flatOptions.otp = '123456' + flatOptions.otp = '123456' const npmProfile = { async get () { diff --git a/deps/npm/test/lib/publish.js b/deps/npm/test/lib/publish.js index 6337a1fcf0..f61377b54f 100644 --- a/deps/npm/test/lib/publish.js +++ b/deps/npm/test/lib/publish.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const fs = require('fs') // The way we set loglevel is kind of convoluted, and there is no way to affect @@ -10,11 +11,13 @@ const log = require('npmlog') log.level = 'silent' // mock config -const {defaults} = require('../../lib/utils/config.js') +const {definitions} = require('../../lib/utils/config') +const defaults = Object.entries(definitions).reduce((defaults, [key, def]) => { + defaults[key] = def.default + return defaults +}, {}) -const config = { - list: [defaults], -} +const config = defaults t.afterEach(cb => { log.level = 'silent' @@ -54,18 +57,17 @@ t.test('should publish with libnpmpublish, passing through flatOptions and respe }, }, }) - const publish = new Publish({ + const npm = mockNpm({ + config, flatOptions: { customValue: true, }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -104,15 +106,12 @@ t.test('re-loads publishConfig.registry if added during script process', (t) => }, }, }) - const publish = new Publish({ - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, - }) + const npm = mockNpm({ config }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -148,21 +147,17 @@ t.test('if loglevel=info and json, should not output package contents', (t) => { }, }, }) - const publish = new Publish({ - flatOptions: { - json: true, - }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, defaults.registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, + const npm = mockNpm({ + config: { ...config, json: true }, output: () => { t.pass('output is called') }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, defaults.registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -198,20 +193,17 @@ t.test('if loglevel=silent and dry-run, should not output package contents or pu }, }, }) - const publish = new Publish({ - flatOptions: { - dryRun: true, - }, - config: { - ...config, - getCredentialsByURI: () => { - throw new Error('should not call getCredentialsByURI in dry run') - }, - }, + const npm = mockNpm({ + config: { ...config, 'dry-run': true }, output: () => { throw new Error('should not output in dry run mode') }, }) + npm.config.getCredentialsByURI = () => { + throw new Error('should not call getCredentialsByURI in dry run') + } + + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -247,20 +239,16 @@ t.test('if loglevel=info and dry-run, should not publish, should log package con }, }, }) - const publish = new Publish({ - flatOptions: { - dryRun: true, - }, - config: { - ...config, - getCredentialsByURI: () => { - throw new Error('should not call getCredentialsByURI in dry run') - }, - }, + const npm = mockNpm({ + config: { ...config, 'dry-run': true }, output: () => { t.pass('output fn is called') }, }) + npm.config.getCredentialsByURI = () => { + throw new Error('should not call getCredentialsByURI in dry run') + } + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -285,12 +273,10 @@ t.test('throws when invalid tag', (t) => { t.plan(1) const Publish = requireInject('../../lib/publish.js') - const publish = new Publish({ - flatOptions: { - defaultTag: '0.0.13', - }, - config, + const npm = mockNpm({ + config: { ...config, tag: '0.0.13' }, }) + const publish = new Publish(npm) publish.exec([], (err) => { t.match(err, { @@ -331,15 +317,12 @@ t.test('can publish a tarball', t => { }, }, }) - const publish = new Publish({ - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, defaults.registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, - }) + const npm = mockNpm({ config }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, defaults.registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) publish.exec([`${testDir}/tarball/package.tgz`], (er) => { if (er) @@ -352,15 +335,12 @@ t.test('can publish a tarball', t => { t.test('should check auth for default registry', t => { t.plan(2) const Publish = requireInject('../../lib/publish.js') - const publish = new Publish({ - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, defaults.registry, 'gets credentials for expected registry') - return {} - }, - }, - }) + const npm = mockNpm({ config }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, defaults.registry, 'gets credentials for expected registry') + return {} + } + const publish = new Publish(npm) publish.exec([], (err) => { t.match(err, { @@ -375,18 +355,15 @@ t.test('should check auth for configured registry', t => { t.plan(2) const registry = 'https://some.registry' const Publish = requireInject('../../lib/publish.js') - const publish = new Publish({ - flatOptions: { - registry, - }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return {} - }, - }, + const npm = mockNpm({ + config, + flatOptions: { registry }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return {} + } + const publish = new Publish(npm) publish.exec([], (err) => { t.match(err, { @@ -408,18 +385,15 @@ t.test('should check auth for scope specific registry', t => { }) const Publish = requireInject('../../lib/publish.js') - const publish = new Publish({ - flatOptions: { - '@npm:registry': registry, - }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return {} - }, - }, + const npm = mockNpm({ + config, + flatOptions: { '@npm:registry': registry }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return {} + } + const publish = new Publish(npm) publish.exec([testDir], (err) => { t.match(err, { @@ -448,18 +422,16 @@ t.test('should use auth for scope specific registry', t => { }, }, }) - const publish = new Publish({ - flatOptions: { - '@npm:registry': registry, - }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, + const npm = mockNpm({ + config, + flatOptions: { '@npm:registry': registry }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) + publish.exec([testDir], (er) => { if (er) throw er @@ -489,9 +461,60 @@ t.test('read registry only from publishConfig', t => { }, }, }) + const npm = mockNpm({ + config, + }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) + + publish.exec([testDir], (er) => { + if (er) + throw er + t.pass('got to callback') + t.end() + }) +}) + +t.test('able to publish after if encountered multiple configs', t => { + t.plan(3) + + const registry = 'https://some.registry' + const tag = 'better-tag' + const publishConfig = { registry } + const testDir = t.testdir({ + 'package.json': JSON.stringify({ + name: 'my-cool-pkg', + version: '1.0.0', + publishConfig, + }, null, 2), + }) + + const configList = [defaults] + configList.unshift(Object.assign(Object.create(configList[0]), { + registry: `https://other.registry`, + tag: 'some-tag', + })) + configList.unshift(Object.assign(Object.create(configList[0]), { tag })) + + const Publish = requireInject('../../lib/publish.js', { + libnpmpublish: { + publish: (manifest, tarData, opts) => { + t.same(opts.defaultTag, tag, 'gets option for expected tag') + }, + }, + }) const publish = new Publish({ + // what would be flattened by the configList created above + flatOptions: { + defaultTag: 'better-tag', + registry: 'https://other.registry', + }, config: { - ...config, + get: key => configList[0][key], + list: configList, getCredentialsByURI: (uri) => { t.same(uri, registry, 'gets credentials for expected registry') return { token: 'some.registry.token' } diff --git a/deps/npm/test/lib/rebuild.js b/deps/npm/test/lib/rebuild.js index 1eb45e0d1d..6e144b7e11 100644 --- a/deps/npm/test/lib/rebuild.js +++ b/deps/npm/test/lib/rebuild.js @@ -1,25 +1,27 @@ const fs = require('fs') const { resolve } = require('path') +const mockNpm = require('../fixtures/mock-npm') const t = require('tap') let result = '' -const npm = { +const config = { + global: false, +} +const npm = mockNpm({ globalDir: '', - flatOptions: { - global: false, - }, + config, prefix: '', output: (...msg) => { result += msg.join('\n') }, -} +}) const Rebuild = require('../../lib/rebuild.js') const rebuild = new Rebuild(npm) t.afterEach(cb => { npm.prefix = '' - npm.flatOptions.global = false + config.global = false npm.globalDir = '' result = '' cb() @@ -34,7 +36,7 @@ t.test('no args', t => { version: '1.0.0', bin: 'cwd', scripts: { - preinstall: `node -e 'require("fs").writeFileSync("cwd", "")'`, + preinstall: "node -e \"require('fs').writeFileSync('cwd', '')\"", }, }), }, @@ -44,7 +46,7 @@ t.test('no args', t => { version: '1.0.0', bin: 'cwd', scripts: { - preinstall: `node -e 'require("fs").writeFileSync("cwd", "")'`, + preinstall: "node -e \"require('fs').writeFileSync('cwd', '')\"", }, }), }, @@ -237,7 +239,7 @@ t.test('global prefix', t => { }, }) - npm.flatOptions.global = true + config.global = true npm.globalDir = resolve(globalPath, 'lib', 'node_modules') rebuild.exec([], err => { diff --git a/deps/npm/test/lib/run-script.js b/deps/npm/test/lib/run-script.js index 0566daf234..f7eb46fedf 100644 --- a/deps/npm/test/lib/run-script.js +++ b/deps/npm/test/lib/run-script.js @@ -1,37 +1,60 @@ +const { resolve } = require('path') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') + +const normalizePath = p => p + .replace(/\\+/g, '/') + .replace(/\r\n/g, '\n') + +const cleanOutput = (str) => normalizePath(str) + .replace(normalizePath(process.cwd()), '{CWD}') const RUN_SCRIPTS = [] -const npm = { +const flatOptions = { + scriptShell: undefined, +} +const config = { + json: false, + parseable: false, + 'if-present': false, +} + +const npm = mockNpm({ localPrefix: __dirname, - flatOptions: { - scriptShell: undefined, - json: false, - parseable: false, - }, - config: { - settings: { - 'if-present': false, + flatOptions, + config, + commands: { + help: { + description: 'test help description', }, - get: k => npm.config.settings[k], - set: (k, v) => { - npm.config.settings[k] = v + test: { + description: 'test test description', }, }, output: (...msg) => output.push(msg), -} +}) const output = [] +const npmlog = { + disableProgress: () => null, + level: 'warn', + error: () => null, +} + t.afterEach(cb => { + npm.color = false + npmlog.level = 'warn' + npmlog.error = () => null output.length = 0 RUN_SCRIPTS.length = 0 - npm.flatOptions.json = false - npm.flatOptions.parseable = false + config['if-present'] = false + config.json = false + config.parseable = false cb() }) -const npmlog = { level: 'warn' } const getRS = windows => { const RunScript = requireInject('../../lib/run-script.js', { '@npmcli/run-script': Object.assign(async opts => { @@ -261,26 +284,41 @@ t.test('try to run missing script', t => { npm.localPrefix = t.testdir({ 'package.json': JSON.stringify({ scripts: { hello: 'world' }, + bin: { goodnight: 'moon' }, }), }) t.test('no suggestions', t => { runScript.exec(['notevenclose'], er => { t.match(er, { - message: 'missing script: notevenclose', + message: 'Missing script: "notevenclose"', }) t.end() }) }) - t.test('suggestions', t => { + t.test('script suggestions', t => { runScript.exec(['helo'], er => { t.match(er, { - message: 'missing script: helo\n\nDid you mean this?\n hello', + message: 'Missing script: "helo"', + }) + t.match(er, { + message: 'npm run hello', + }) + t.end() + }) + }) + t.test('bin suggestions', t => { + runScript.exec(['goodneght'], er => { + t.match(er, { + message: 'Missing script: "goodneght"', + }) + t.match(er, { + message: 'npm exec goodnight', }) t.end() }) }) t.test('with --if-present', t => { - npm.config.set('if-present', true) + config['if-present'] = true runScript.exec(['goodbye'], er => { if (er) throw er @@ -331,7 +369,7 @@ t.test('run pre/post hooks', t => { }) t.test('skip pre/post hooks when using ignoreScripts', t => { - npm.flatOptions.ignoreScripts = true + config['ignore-scripts'] = true npm.localPrefix = t.testdir({ 'package.json': JSON.stringify({ @@ -368,7 +406,7 @@ t.test('skip pre/post hooks when using ignoreScripts', t => { }, ]) t.end() - delete npm.flatOptions.ignoreScripts + delete config['ignore-scripts'] }) }) @@ -443,13 +481,14 @@ t.test('list scripts', t => { if (er) throw er t.strictSame(output, [ - ['Lifecycle scripts included in x:'], + ['Lifecycle scripts included in x@1.2.3:'], [' test\n exit 2'], [' start\n node server.js'], [' stop\n node kill-server.js'], ['\navailable via `npm run-script`:'], [' preenv\n echo before the env'], [' postenv\n echo after the env'], + [''], ], 'basic report') t.end() }) @@ -466,7 +505,7 @@ t.test('list scripts', t => { }) t.test('warn json', t => { npmlog.level = 'warn' - npm.flatOptions.json = true + config.json = true runScript.exec([], er => { if (er) throw er @@ -476,7 +515,7 @@ t.test('list scripts', t => { }) t.test('parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true runScript.exec([], er => { if (er) throw er @@ -522,8 +561,9 @@ t.test('list scripts, only commands', t => { if (er) throw er t.strictSame(output, [ - ['Lifecycle scripts included in x:'], + ['Lifecycle scripts included in x@1.2.3:'], [' preversion\n echo doing the version dance'], + [''], ]) t.end() }) @@ -542,9 +582,443 @@ t.test('list scripts, only non-commands', t => { if (er) throw er t.strictSame(output, [ - ['Scripts available in x via `npm run-script`:'], + ['Scripts available in x@1.2.3 via `npm run-script`:'], [' glorp\n echo doing the glerp glop'], + [''], ]) t.end() }) }) + +t.test('workspaces', t => { + npm.localPrefix = t.testdir({ + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + scripts: { glorp: 'echo a doing the glerp glop' }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '2.0.0', + scripts: { glorp: 'echo b doing the glerp glop' }, + }), + }, + c: { + 'package.json': JSON.stringify({ + name: 'c', + version: '1.0.0', + scripts: { + test: 'exit 0', + posttest: 'echo posttest', + lorem: 'echo c lorem', + }, + }), + }, + d: { + 'package.json': JSON.stringify({ + name: 'd', + version: '1.0.0', + scripts: { + test: 'exit 0', + posttest: 'echo posttest', + }, + }), + }, + e: { + 'package.json': JSON.stringify({ + name: 'e', + scripts: { test: 'exit 0', start: 'echo start something' }, + }), + }, + noscripts: { + 'package.json': JSON.stringify({ + name: 'noscripts', + version: '1.0.0', + }), + }, + }, + 'package.json': JSON.stringify({ + name: 'x', + version: '1.2.3', + workspaces: ['packages/*'], + }), + }) + + t.test('list all scripts', t => { + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, [ + ['Scripts available in a@1.0.0 via `npm run-script`:'], + [' glorp\n echo a doing the glerp glop'], + [''], + ['Scripts available in b@2.0.0 via `npm run-script`:'], + [' glorp\n echo b doing the glerp glop'], + [''], + ['Lifecycle scripts included in c@1.0.0:'], + [' test\n exit 0'], + [' posttest\n echo posttest'], + ['\navailable via `npm run-script`:'], + [' lorem\n echo c lorem'], + [''], + ['Lifecycle scripts included in d@1.0.0:'], + [' test\n exit 0'], + [' posttest\n echo posttest'], + [''], + ['Lifecycle scripts included in e:'], + [' test\n exit 0'], + [' start\n echo start something'], + [''], + ]) + t.end() + }) + }) + + t.test('list regular scripts, filtered by name', t => { + runScript.execWorkspaces([], ['a', 'b'], er => { + if (er) + throw er + t.strictSame(output, [ + ['Scripts available in a@1.0.0 via `npm run-script`:'], + [' glorp\n echo a doing the glerp glop'], + [''], + ['Scripts available in b@2.0.0 via `npm run-script`:'], + [' glorp\n echo b doing the glerp glop'], + [''], + ]) + t.end() + }) + }) + + t.test('list regular scripts, filtered by path', t => { + runScript.execWorkspaces([], ['./packages/a'], er => { + if (er) + throw er + t.strictSame(output, [ + ['Scripts available in a@1.0.0 via `npm run-script`:'], + [' glorp\n echo a doing the glerp glop'], + [''], + ]) + t.end() + }) + }) + + t.test('list regular scripts, filtered by parent folder', t => { + runScript.execWorkspaces([], ['./packages'], er => { + if (er) + throw er + t.strictSame(output, [ + ['Scripts available in a@1.0.0 via `npm run-script`:'], + [' glorp\n echo a doing the glerp glop'], + [''], + ['Scripts available in b@2.0.0 via `npm run-script`:'], + [' glorp\n echo b doing the glerp glop'], + [''], + ['Lifecycle scripts included in c@1.0.0:'], + [' test\n exit 0'], + [' posttest\n echo posttest'], + ['\navailable via `npm run-script`:'], + [' lorem\n echo c lorem'], + [''], + ['Lifecycle scripts included in d@1.0.0:'], + [' test\n exit 0'], + [' posttest\n echo posttest'], + [''], + ['Lifecycle scripts included in e:'], + [' test\n exit 0'], + [' start\n echo start something'], + [''], + ]) + t.end() + }) + }) + + t.test('list all scripts with colors', t => { + npm.color = true + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, [ + [ + '\u001b[1mScripts\u001b[22m available in \x1B[32ma@1.0.0\x1B[39m via `\x1B[34mnpm run-script\x1B[39m`:', + ], + [' glorp\n \x1B[2mecho a doing the glerp glop\x1B[22m'], + [''], + [ + '\u001b[1mScripts\u001b[22m available in \x1B[32mb@2.0.0\x1B[39m via `\x1B[34mnpm run-script\x1B[39m`:', + ], + [' glorp\n \x1B[2mecho b doing the glerp glop\x1B[22m'], + [''], + [ + '\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32mc@1.0.0\x1B[39m:', + ], + [' test\n \x1B[2mexit 0\x1B[22m'], + [' posttest\n \x1B[2mecho posttest\x1B[22m'], + ['\navailable via `\x1B[34mnpm run-script\x1B[39m`:'], + [' lorem\n \x1B[2mecho c lorem\x1B[22m'], + [''], + [ + '\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32md@1.0.0\x1B[39m:', + ], + [' test\n \x1B[2mexit 0\x1B[22m'], + [' posttest\n \x1B[2mecho posttest\x1B[22m'], + [''], + [ + '\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32me\x1B[39m:', + ], + [' test\n \x1B[2mexit 0\x1B[22m'], + [' start\n \x1B[2mecho start something\x1B[22m'], + [''], + ]) + t.end() + }) + }) + + t.test('list all scripts --json', t => { + config.json = true + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, [ + [ + '{\n' + + ' "a": {\n' + + ' "glorp": "echo a doing the glerp glop"\n' + + ' },\n' + + ' "b": {\n' + + ' "glorp": "echo b doing the glerp glop"\n' + + ' },\n' + + ' "c": {\n' + + ' "test": "exit 0",\n' + + ' "posttest": "echo posttest",\n' + + ' "lorem": "echo c lorem"\n' + + ' },\n' + + ' "d": {\n' + + ' "test": "exit 0",\n' + + ' "posttest": "echo posttest"\n' + + ' },\n' + + ' "e": {\n' + + ' "test": "exit 0",\n' + + ' "start": "echo start something"\n' + + ' },\n' + + ' "noscripts": {}\n' + + '}', + ], + ]) + t.end() + }) + }) + + t.test('list all scripts --parseable', t => { + config.parseable = true + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, [ + ['a:glorp:echo a doing the glerp glop'], + ['b:glorp:echo b doing the glerp glop'], + ['c:test:exit 0'], + ['c:posttest:echo posttest'], + ['c:lorem:echo c lorem'], + ['d:test:exit 0'], + ['d:posttest:echo posttest'], + ['e:test:exit 0'], + ['e:start:echo start something'], + ]) + t.end() + }) + }) + + t.test('list no scripts --loglevel=silent', t => { + npmlog.level = 'silent' + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, []) + t.end() + }) + }) + + t.test('run scripts across all workspaces', t => { + runScript.execWorkspaces(['test'], [], er => { + if (er) + throw er + + t.match(RUN_SCRIPTS, [ + { + path: resolve(npm.localPrefix, 'packages/c'), + pkg: { name: 'c', version: '1.0.0' }, + event: 'test', + }, + { + path: resolve(npm.localPrefix, 'packages/c'), + pkg: { name: 'c', version: '1.0.0' }, + event: 'posttest', + }, + { + path: resolve(npm.localPrefix, 'packages/d'), + pkg: { name: 'd', version: '1.0.0' }, + event: 'test', + }, + { + path: resolve(npm.localPrefix, 'packages/d'), + pkg: { name: 'd', version: '1.0.0' }, + event: 'posttest', + }, + { + path: resolve(npm.localPrefix, 'packages/e'), + pkg: { name: 'e' }, + event: 'test', + }, + ]) + t.end() + }) + }) + + t.test('missing scripts in all workspaces', t => { + const LOG = [] + npmlog.error = (err) => { + LOG.push(String(err)) + } + runScript.execWorkspaces(['missing-script'], [], er => { + t.match( + er, + /Missing script: missing-script/, + 'should throw missing script error' + ) + + process.exitCode = 0 // clean exit code + + t.match(RUN_SCRIPTS, []) + t.strictSame(LOG.map(cleanOutput), [ + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: a@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/a', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: b@2.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/b', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: c@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/c', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: d@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/d', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: e', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/e', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: noscripts@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/noscripts', + ], 'should log error msgs for each workspace script') + + t.end() + }) + }) + + t.test('missing scripts in some workspaces', t => { + const LOG = [] + npmlog.error = (err) => { + LOG.push(String(err)) + } + runScript.execWorkspaces(['test'], ['a', 'b', 'c', 'd'], er => { + if (er) + throw er + + t.match(RUN_SCRIPTS, []) + t.strictSame(LOG.map(cleanOutput), [ + 'Lifecycle script `test` failed with error:', + 'Error: Missing script: "test"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: a@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/a', + 'Lifecycle script `test` failed with error:', + 'Error: Missing script: "test"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: b@2.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/b', + ], 'should log error msgs for each workspace script') + t.end() + }) + }) + + t.test('no workspaces when filtering by user args', t => { + runScript.execWorkspaces([], ['foo', 'bar'], er => { + t.equal( + er.message, + 'No workspaces found:\n --workspace=foo --workspace=bar', + 'should throw error msg' + ) + t.end() + }) + }) + + t.test('no workspaces', t => { + const _prevPrefix = npm.localPrefix + npm.localPrefix = t.testdir({ + 'package.json': JSON.stringify({ + name: 'foo', + version: '1.0.0', + }), + }) + + runScript.execWorkspaces([], [], er => { + t.match(er, /No workspaces found!/, 'should throw error msg') + npm.localPrefix = _prevPrefix + t.end() + }) + }) + + t.test('single failed workspace run', t => { + const RunScript = requireInject('../../lib/run-script.js', { + '@npmcli/run-script': () => { + throw new Error('err') + }, + npmlog, + '../../lib/utils/is-windows-shell.js': false, + }) + const runScript = new RunScript(npm) + + runScript.execWorkspaces(['test'], ['c'], er => { + t.ok('should complete running all targets') + process.exitCode = 0 // clean up exit code + t.end() + }) + }) + + t.test('failed workspace run with succeeded runs', t => { + const RunScript = requireInject('../../lib/run-script.js', { + '@npmcli/run-script': async opts => { + if (opts.pkg.name === 'a') + throw new Error('ERR') + + RUN_SCRIPTS.push(opts) + }, + npmlog, + '../../lib/utils/is-windows-shell.js': false, + }) + const runScript = new RunScript(npm) + + runScript.execWorkspaces(['glorp'], ['a', 'b'], er => { + t.match(RUN_SCRIPTS, [ + { + path: resolve(npm.localPrefix, 'packages/b'), + pkg: { name: 'b', version: '2.0.0' }, + event: 'glorp', + }, + ]) + + process.exitCode = 0 // clean up exit code + t.end() + }) + }) + + t.end() +}) diff --git a/deps/npm/test/lib/search.js b/deps/npm/test/lib/search.js index b1ba7775c7..b7b4084421 100644 --- a/deps/npm/test/lib/search.js +++ b/deps/npm/test/lib/search.js @@ -1,6 +1,7 @@ const Minipass = require('minipass') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const libnpmsearchResultFixture = require('../fixtures/libnpmsearch-stream-result.js') @@ -12,12 +13,17 @@ const flatOptions = { opts: '', }, } -const npm = { +const config = { + json: false, + parseable: false, +} +const npm = mockNpm({ + config, flatOptions: { ...flatOptions }, output: (...msg) => { result += msg.join('\n') }, -} +}) const npmlog = { silly () {}, clearProgress () {}, @@ -29,12 +35,13 @@ const mocks = { npmlog, libnpmsearch, '../../lib/utils/usage.js': () => 'usage instructions', - // '../../lib/search/format-package-stream.js': a => a, } t.afterEach(cb => { result = '' - npm.flatOptions = flatOptions + config.json = false + config.parseable = false + npm.flatOptions = { ...flatOptions } cb() }) @@ -86,7 +93,8 @@ t.test('search <name> --json', (t) => { const src = new Minipass() src.objectMode = true - flatOptions.json = true + npm.flatOptions.json = true + config.json = true const libnpmsearch = { stream () { return src @@ -114,7 +122,7 @@ t.test('search <name> --json', (t) => { 'should have expected search results as json' ) - flatOptions.json = false + config.json = false t.end() }) diff --git a/deps/npm/test/lib/set-script.js b/deps/npm/test/lib/set-script.js index 7a057c5036..b6b6e2519f 100644 --- a/deps/npm/test/lib/set-script.js +++ b/deps/npm/test/lib/set-script.js @@ -31,6 +31,7 @@ test.test('fails when package.json not found', (t) => { }) test.test('fails on invalid JSON', (t) => { const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFile: () => {}, // read-package-json-fast explodes w/o this readFileSync: (name, charcode) => { @@ -45,6 +46,7 @@ test.test('fails on invalid JSON', (t) => { test.test('creates scripts object', (t) => { var mockFile = '' const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFileSync: (name, charcode) => { return '{}' @@ -70,6 +72,7 @@ test.test('creates scripts object', (t) => { test.test('warns before overwriting', (t) => { var warningListened = '' const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFileSync: (name, charcode) => { return JSON.stringify({ @@ -102,6 +105,7 @@ test.test('warns before overwriting', (t) => { test.test('provided indentation and eol is used', (t) => { var mockFile = '' const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFileSync: (name, charcode) => { return '{}' @@ -128,6 +132,7 @@ test.test('provided indentation and eol is used', (t) => { test.test('goes to default when undefined indent and eol provided', (t) => { var mockFile = '' const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFileSync: (name, charcode) => { return '{}' diff --git a/deps/npm/test/lib/shrinkwrap.js b/deps/npm/test/lib/shrinkwrap.js index dc4bc3b220..faf452ea70 100644 --- a/deps/npm/test/lib/shrinkwrap.js +++ b/deps/npm/test/lib/shrinkwrap.js @@ -1,16 +1,21 @@ const t = require('tap') const fs = require('fs') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') -const npm = { +const config = { + global: false, +} +const flatOptions = { + depth: 0, +} +const npm = mockNpm({ + config, + flatOptions, lockfileVersion: 2, globalDir: '', - flatOptions: { - depth: 0, - global: false, - }, prefix: '', -} +}) const tree = { meta: { hiddenLockfile: null, @@ -32,11 +37,12 @@ const mocks = { } }, '../../lib/utils/usage.js': () => 'usage instructions', + '../../lib/utils/config/definitions.js': {}, } t.afterEach(cb => { npm.prefix = '' - npm.flatOptions.global = false + config.global = false npm.globalDir = '' cb() }) @@ -50,7 +56,7 @@ t.test('no args', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -101,7 +107,7 @@ t.test('no virtual tree', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -156,7 +162,7 @@ t.test('existing package-json file', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -218,7 +224,7 @@ t.test('update shrinkwrap file version', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -272,7 +278,7 @@ t.test('update to date shrinkwrap file', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -320,7 +326,7 @@ t.test('update to date shrinkwrap file', t => { t.test('shrinkwrap --global', t => { const Shrinkwrap = requireInject('../../lib/shrinkwrap.js', mocks) - npm.flatOptions.global = true + config.global = true const shrinkwrap = new Shrinkwrap(npm) shrinkwrap.exec([], err => { diff --git a/deps/npm/test/lib/star.js b/deps/npm/test/lib/star.js index 774fabe392..fa75d21057 100644 --- a/deps/npm/test/lib/star.js +++ b/deps/npm/test/lib/star.js @@ -1,16 +1,20 @@ const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const t = require('tap') let result = '' const noop = () => null -const npm = { - config: { get () {} }, - flatOptions: { unicode: false }, +const config = { + unicode: false, + 'star.unstar': false, +} +const npm = mockNpm({ + config, output: (...msg) => { result += msg.join('\n') }, -} +}) const npmFetch = { json: noop } const npmlog = { error: noop, info: noop, verbose: noop } const mocks = { @@ -24,8 +28,8 @@ const Star = requireInject('../../lib/star.js', mocks) const star = new Star(npm) t.afterEach(cb => { - npm.config = { get () {} } - npm.flatOptions.unicode = false + config.unicode = false + config['star.unstar'] = false npmlog.info = noop result = '' cb() @@ -73,7 +77,7 @@ t.test('star a package', t => { t.test('unstar a package', t => { t.plan(4) const pkgName = '@npmcli/arborist' - npm.config.get = key => key === 'star.unstar' + config['star.unstar'] = true npmFetch.json = async (uri, opts) => ({ _id: pkgName, _rev: 'hash', @@ -100,7 +104,7 @@ t.test('unstar a package', t => { t.test('unicode', async t => { t.test('star a package', t => { - npm.flatOptions.unicode = true + config.unicode = true npmFetch.json = async (uri, opts) => ({}) star.exec(['pkg'], err => { if (err) @@ -115,8 +119,8 @@ t.test('unicode', async t => { }) t.test('unstar a package', t => { - npm.flatOptions.unicode = true - npm.config.get = key => key === 'star.unstar' + config.unicode = true + config['star.unstar'] = true npmFetch.json = async (uri, opts) => ({}) star.exec(['pkg'], err => { if (err) diff --git a/deps/npm/test/lib/uninstall.js b/deps/npm/test/lib/uninstall.js index c62b59950b..5cb8a243ec 100644 --- a/deps/npm/test/lib/uninstall.js +++ b/deps/npm/test/lib/uninstall.js @@ -2,18 +2,18 @@ const fs = require('fs') const { resolve } = require('path') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') -const npm = { +const npm = mockNpm({ globalDir: '', - flatOptions: { + config: { global: false, prefix: '', }, localPrefix: '', -} +}) const mocks = { '../../lib/utils/reify-finish.js': () => Promise.resolve(), - '../../lib/utils/usage.js': () => 'usage instructions', } const Uninstall = requireInject('../../lib/uninstall.js', mocks) @@ -85,13 +85,13 @@ t.test('remove single installed lib', t => { const b = resolve(path, 'node_modules/b') t.ok(() => fs.statSync(b)) - npm.flatOptions.prefix = path + npm.config.set('prefix', path) uninstall.exec(['b'], err => { if (err) throw err - t.throws(() => fs.statSync(b), 'should have removed package from nm') + t.throws(() => fs.statSync(b), 'should have removed package from npm') t.end() }) }) @@ -148,7 +148,7 @@ t.test('remove multiple installed libs', t => { t.ok(() => fs.statSync(a)) t.ok(() => fs.statSync(b)) - npm.flatOptions.prefix = path + npm.config.set('prefix', path) uninstall.exec(['b'], err => { if (err) @@ -195,8 +195,8 @@ t.test('no args global', t => { npm.localPrefix = resolve(path, 'projects', 'a') npm.globalDir = resolve(path, 'lib', 'node_modules') - npm.flatOptions.global = true - npm.flatOptions.prefix = path + npm.config.set('global', true) + npm.config.set('prefix', path) const a = resolve(path, 'lib/node_modules/a') t.ok(() => fs.statSync(a)) @@ -221,8 +221,7 @@ t.test('no args global but no package.json', t => { uninstall.exec([], err => { t.match( err, - 'usage instructions', - 'should throw usage instructions' + 'npm uninstall' ) t.end() diff --git a/deps/npm/test/lib/unpublish.js b/deps/npm/test/lib/unpublish.js index b1255b94a8..ba99b53303 100644 --- a/deps/npm/test/lib/unpublish.js +++ b/deps/npm/test/lib/unpublish.js @@ -1,19 +1,21 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let result = '' const noop = () => null -const npm = { +const config = { + force: false, + silent: false, + loglevel: 'silly', +} +const npm = mockNpm({ localPrefix: '', - flatOptions: { - force: false, - silent: false, - loglevel: 'silly', - }, + config, output: (...msg) => { result += msg.join('\n') }, -} +}) const mocks = { npmlog: { silly () {}, verbose () {} }, libnpmaccess: { lsPackages: noop }, @@ -28,16 +30,16 @@ const mocks = { t.afterEach(cb => { result = '' - npm.flatOptions.force = false - npm.flatOptions.loglevel = 'silly' - npm.flatOptions.silent = false + config.force = false + config.loglevel = 'silly' + config.silent = false cb() }) t.test('no args --force', t => { t.plan(9) - npm.flatOptions.force = true + config.force = true const npmlog = { silly (title) { @@ -67,9 +69,6 @@ t.test('no args --force', t => { t.deepEqual( opts, { - force: true, - silent: false, - loglevel: 'silly', publishConfig: undefined, }, 'should unpublish with expected opts' @@ -102,7 +101,7 @@ t.test('no args --force', t => { }) t.test('no args --force missing package.json', t => { - npm.flatOptions.force = true + config.force = true const Unpublish = requireInject('../../lib/unpublish.js', { ...mocks, @@ -124,7 +123,7 @@ t.test('no args --force missing package.json', t => { }) t.test('no args --force unknown error reading package.json', t => { - npm.flatOptions.force = true + config.force = true const Unpublish = requireInject('../../lib/unpublish.js', { ...mocks, @@ -200,11 +199,7 @@ t.test('unpublish <pkg>@version', t => { t.equal(spec, pa, 'should unpublish expected parsed spec') t.deepEqual( opts, - { - force: false, - silent: false, - loglevel: 'silly', - }, + {}, 'should unpublish with expected opts' ) }, @@ -231,7 +226,7 @@ t.test('unpublish <pkg>@version', t => { }) t.test('no version found in package.json', t => { - npm.flatOptions.force = true + config.force = true const npa = () => ({ name: 'pkg', @@ -263,7 +258,7 @@ t.test('no version found in package.json', t => { }) t.test('unpublish <pkg> --force no version set', t => { - npm.flatOptions.force = true + config.force = true const Unpublish = requireInject('../../lib/unpublish.js', { ...mocks, @@ -289,7 +284,7 @@ t.test('unpublish <pkg> --force no version set', t => { }) t.test('silent', t => { - npm.flatOptions.loglevel = 'silent' + config.loglevel = 'silent' const npa = () => ({ name: 'pkg', diff --git a/deps/npm/test/lib/update.js b/deps/npm/test/lib/update.js index 15195573f5..695218a7f6 100644 --- a/deps/npm/test/lib/update.js +++ b/deps/npm/test/lib/update.js @@ -1,16 +1,18 @@ const { resolve } = require('path') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') +const config = { + depth: 0, + global: false, +} const noop = () => null -const npm = { +const npm = mockNpm({ globalDir: '', - flatOptions: { - depth: 0, - global: false, - }, + config, prefix: '', -} +}) const mocks = { npmlog: { warn () {} }, '@npmcli/arborist': class { @@ -22,7 +24,7 @@ const mocks = { t.afterEach(cb => { npm.prefix = '' - npm.flatOptions.global = false + config.global = false npm.globalDir = '' cb() }) @@ -99,7 +101,7 @@ t.test('update --depth=<number>', t => { t.plan(2) npm.prefix = '/project/a' - npm.flatOptions.depth = 1 + config.depth = 1 const Update = requireInject('../../lib/update.js', { ...mocks, @@ -131,7 +133,7 @@ t.test('update --global', t => { npm.prefix = '/project/a' npm.globalDir = resolve(process.cwd(), 'global/lib/node_modules') - npm.flatOptions.global = true + config.global = true class Arborist { constructor (args) { diff --git a/deps/npm/test/lib/utils/config.js b/deps/npm/test/lib/utils/config.js deleted file mode 100644 index 4d4b1a1d1a..0000000000 --- a/deps/npm/test/lib/utils/config.js +++ /dev/null @@ -1,143 +0,0 @@ -const t = require('tap') -const requireInject = require('require-inject') - -// have to fake the node version, or else it'll only pass on this one -Object.defineProperty(process, 'version', { - value: 'v14.8.0', -}) - -t.formatSnapshot = obj => { - if (typeof obj !== 'object' || !obj || !obj.types) - return obj - - return { - ...obj, - defaults: { - ...obj.defaults, - cache: '{CACHE DIR} ' + path.basename(obj.defaults.cache), - }, - types: formatTypes(obj.types), - } -} - -const path = require('path') -const url = require('url') -const semver = require('semver') - -const formatTypes = (types) => Object.entries(types).map(([key, value]) => { - return [key, formatTypeValue(value)] -}).reduce((set, [key, value]) => { - set[key] = value - return set -}, {}) - -const formatTypeValue = (value) => { - if (Array.isArray(value)) - return value.map(formatTypeValue) - else if (value === url) - return '{URL MODULE}' - else if (value === path) - return '{PATH MODULE}' - else if (value === semver) - return '{SEMVER MODULE}' - else if (typeof value === 'function') - return `{${value.name} TYPE}` - else - return value -} - -process.env.ComSpec = 'cmd.exe' -process.env.SHELL = '/usr/local/bin/bash' -process.env.LANG = 'UTF-8' -delete process.env.LC_ALL -delete process.env.LC_CTYPE -process.env.EDITOR = 'vim' -process.env.VISUAL = 'mate' - -const networkInterfacesThrow = () => { - throw new Error('no network interfaces for some reason') -} -const networkInterfaces = () => ({ - eth420: [{ address: '127.0.0.1' }], - eth69: [{ address: 'no place like home' }], -}) -const tmpdir = () => '/tmp' -const os = { networkInterfaces, tmpdir } -const pkg = { version: '7.0.0' } - -t.test('working network interfaces, not windows', t => { - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': false, - '../../../package.json': pkg, - }) - t.matchSnapshot(config) - t.end() -}) - -t.test('no working network interfaces, on windows', t => { - const config = requireInject('../../../lib/utils/config.js', { - os: { tmpdir, networkInterfaces: networkInterfacesThrow }, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': true, - '../../../package.json': pkg, - }) - t.matchSnapshot(config) - t.end() -}) - -t.test('no comspec on windows', t => { - delete process.env.ComSpec - const config = requireInject('../../../lib/utils/config.js', { - os: { tmpdir, networkInterfaces: networkInterfacesThrow }, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': true, - }) - t.equal(config.defaults.shell, 'cmd') - t.end() -}) - -t.test('no shell on posix', t => { - delete process.env.SHELL - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': false, - }) - t.equal(config.defaults.shell, 'sh') - t.end() -}) - -t.test('no EDITOR env, use VISUAL', t => { - delete process.env.EDITOR - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': false, - }) - t.equal(config.defaults.editor, 'mate') - t.end() -}) - -t.test('no VISUAL, use system default, not windows', t => { - delete process.env.VISUAL - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': false, - }) - t.equal(config.defaults.editor, 'vi') - t.end() -}) - -t.test('no VISUAL, use system default, not windows', t => { - delete process.env.VISUAL - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': true, - }) - t.equal(config.defaults.editor, 'notepad.exe') - t.end() -}) diff --git a/deps/npm/test/lib/utils/config/definition.js b/deps/npm/test/lib/utils/config/definition.js new file mode 100644 index 0000000000..56e10da0cb --- /dev/null +++ b/deps/npm/test/lib/utils/config/definition.js @@ -0,0 +1,185 @@ +const t = require('tap') +const Definition = require('../../../../lib/utils/config/definition.js') +const { + typeDefs: { + semver: { type: semver }, + Umask: { type: Umask }, + url: { type: url }, + path: { type: path }, + }, +} = require('@npmcli/config') + +t.test('basic definition', async t => { + const def = new Definition('key', { + default: 'some default value', + type: [Number, String], + description: 'just a test thingie', + }) + t.same(def, { + constructor: Definition, + key: 'key', + default: 'some default value', + defaultDescription: '"some default value"', + type: [Number, String], + hint: '<key>', + usage: '--key <key>|--key <key>', + typeDescription: 'Number or String', + description: 'just a test thingie', + }) + t.matchSnapshot(def.describe(), 'human-readable description') + + const deprecated = new Definition('deprecated', { + deprecated: 'do not use this', + default: 1234, + description: ' it should not be used\n ever\n\n not even once.\n\n', + type: Number, + defaultDescription: 'A number bigger than 1', + typeDescription: 'An expression of a numeric quantity using numerals', + }) + t.matchSnapshot(deprecated.describe(), 'description of deprecated thing') + + const nullOrUmask = new Definition('key', { + default: null, + type: [null, Umask], + description: 'asdf', + }) + t.equal(nullOrUmask.typeDescription, 'null or Octal numeric string in range 0000..0777 (0..511)') + const nullDateOrBool = new Definition('key', { + default: 7, + type: [null, Date, Boolean], + description: 'asdf', + }) + t.equal(nullDateOrBool.typeDescription, 'null, Date, or Boolean') + const manyPaths = new Definition('key', { + default: ['asdf'], + type: [path, Array], + description: 'asdf', + }) + t.equal(manyPaths.typeDescription, 'Path (can be set multiple times)') + const pathOrUrl = new Definition('key', { + default: ['https://example.com'], + type: [path, url], + description: 'asdf', + }) + t.equal(pathOrUrl.typeDescription, 'Path or URL') + const multi12 = new Definition('key', { + default: [], + type: [1, 2, Array], + description: 'asdf', + }) + t.equal(multi12.typeDescription, '1 or 2 (can be set multiple times)') + const multi123 = new Definition('key', { + default: [], + type: [1, 2, 3, Array], + description: 'asdf', + }) + t.equal(multi123.typeDescription, '1, 2, or 3 (can be set multiple times)') + const multi123Semver = new Definition('key', { + default: [], + type: [1, 2, 3, Array, semver], + description: 'asdf', + }) + t.equal(multi123Semver.typeDescription, '1, 2, 3, or SemVer string (can be set multiple times)') + const hasUsage = new Definition('key', { + default: 'test default', + type: String, + description: 'test description', + usage: 'test usage', + }) + t.equal(hasUsage.usage, 'test usage') + const hasShort = new Definition('key', { + default: 'test default', + short: 't', + type: String, + description: 'test description', + }) + t.equal(hasShort.usage, '-t|--key <key>') + const hardCodedTypes = new Definition('key', { + default: 'test default', + type: ['string1', 'string2'], + description: 'test description', + }) + t.equal(hardCodedTypes.usage, '--key <string1|string2>') + const hardCodedOptionalTypes = new Definition('key', { + default: 'test default', + type: [null, 'string1', 'string2'], + description: 'test description', + }) + t.equal(hardCodedOptionalTypes.usage, '--key <string1|string2>') + const hasHint = new Definition('key', { + default: 'test default', + type: String, + description: 'test description', + hint: '<testparam>', + }) + t.equal(hasHint.usage, '--key <testparam>') +}) + +t.test('missing fields', async t => { + t.throws(() => new Definition('lacks-default', { + description: 'no default', + type: String, + }), { message: 'config lacks default: lacks-default' }) + t.throws(() => new Definition('lacks-type', { + description: 'no type', + default: 1234, + }), { message: 'config lacks type: lacks-type' }) + t.throws(() => new Definition(null, { + description: 'falsey key', + default: 1234, + type: Number, + }), { message: 'config lacks key: null' }) + t.throws(() => new Definition('extra-field', { + type: String, + default: 'extra', + extra: 'more than is wanted', + description: 'this is not ok', + }), { message: 'config defines unknown field extra: extra-field' }) +}) + +t.test('long description', async t => { + const { stdout: { columns } } = process + t.teardown(() => process.stdout.columns = columns) + + const long = new Definition('walden', { + description: ` + WHEN I WROTE the following pages, or rather the bulk of them, I lived + alone, in the woods, a mile from any neighbor, in a house which I had + built myself, on the shore of Walden Pond, in Concord, Massachusetts, and + earned my living by the labor of my hands only. I lived there two years + and two months. At present I am a sojourner in civilized life again. + + I should not obtrude my affairs so much on the notice of my readers if + very particular inquiries had not been made by my townsmen concerning my + mode of life, which some would call impertinent, though they do not + appear to me at all impertinent, but, considering the circumstances, very + natural and pertinent. + + \`\`\` + this.is('a', { + code: 'sample', + }) + + with (multiple) { + blocks() + } + \`\`\` + `, + default: true, + type: Boolean, + }) + process.stdout.columns = 40 + t.matchSnapshot(long.describe(), 'cols=40') + + process.stdout.columns = 9000 + t.matchSnapshot(long.describe(), 'cols=9000') + + process.stdout.columns = 0 + t.matchSnapshot(long.describe(), 'cols=0') + + process.stdout.columns = -1 + t.matchSnapshot(long.describe(), 'cols=-1') + + process.stdout.columns = NaN + t.matchSnapshot(long.describe(), 'cols=NaN') +}) diff --git a/deps/npm/test/lib/utils/config/definitions.js b/deps/npm/test/lib/utils/config/definitions.js new file mode 100644 index 0000000000..3169feefb8 --- /dev/null +++ b/deps/npm/test/lib/utils/config/definitions.js @@ -0,0 +1,697 @@ +const t = require('tap') + +const requireInject = require('require-inject') +const { resolve } = require('path') + +// have to fake the node version, or else it'll only pass on this one +Object.defineProperty(process, 'version', { + value: 'v14.8.0', +}) + +// also fake the npm version, so that it doesn't get reset every time +const pkg = require('../../../../package.json') + +// this is a pain to keep typing +const defpath = '../../../../lib/utils/config/definitions.js' + +// set this in the test when we need it +delete process.env.NODE_ENV +const definitions = require(defpath) + +const isWin = '../../../../lib/utils/is-windows.js' + +// snapshot these just so we note when they change +t.matchSnapshot(Object.keys(definitions), 'all config keys') +t.matchSnapshot(Object.keys(definitions).filter(d => d.flatten), + 'all config keys that are shared to flatOptions') + +t.equal(definitions['npm-version'].default, pkg.version, 'npm-version default') +t.equal(definitions['node-version'].default, process.version, 'node-version default') + +t.test('basic flattening function camelCases from css-case', t => { + const flat = {} + const obj = { 'always-auth': true } + definitions['always-auth'].flatten('always-auth', obj, flat) + t.strictSame(flat, { alwaysAuth: true }) + t.end() +}) + +t.test('editor', t => { + t.test('has EDITOR and VISUAL, use EDITOR', t => { + process.env.EDITOR = 'vim' + process.env.VISUAL = 'mate' + const defs = requireInject(defpath) + t.equal(defs.editor.default, 'vim') + t.end() + }) + t.test('has VISUAL but no EDITOR, use VISUAL', t => { + delete process.env.EDITOR + process.env.VISUAL = 'mate' + const defs = requireInject(defpath) + t.equal(defs.editor.default, 'mate') + t.end() + }) + t.test('has neither EDITOR nor VISUAL, system specific', t => { + delete process.env.EDITOR + delete process.env.VISUAL + const defsWin = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsWin.editor.default, 'notepad.exe') + const defsNix = requireInject(defpath, { + [isWin]: false, + }) + t.equal(defsNix.editor.default, 'vi') + t.end() + }) + t.end() +}) + +t.test('shell', t => { + t.test('windows, env.ComSpec then cmd.exe', t => { + process.env.ComSpec = 'command.com' + const defsComSpec = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsComSpec.shell.default, 'command.com') + delete process.env.ComSpec + const defsNoComSpec = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsNoComSpec.shell.default, 'cmd') + t.end() + }) + + t.test('nix, SHELL then sh', t => { + process.env.SHELL = '/usr/local/bin/bash' + const defsShell = requireInject(defpath, { + [isWin]: false, + }) + t.equal(defsShell.shell.default, '/usr/local/bin/bash') + delete process.env.SHELL + const defsNoShell = requireInject(defpath, { + [isWin]: false, + }) + t.equal(defsNoShell.shell.default, 'sh') + t.end() + }) + + t.end() +}) + +t.test('local-address allowed types', t => { + t.test('get list from os.networkInterfaces', t => { + const os = { + tmpdir: () => '/tmp', + networkInterfaces: () => ({ + eth420: [{ address: '127.0.0.1' }], + eth69: [{ address: 'no place like home' }], + }), + } + const defs = requireInject(defpath, { os }) + t.same(defs['local-address'].type, [ + null, + '127.0.0.1', + 'no place like home', + ]) + t.end() + }) + t.test('handle os.networkInterfaces throwing', t => { + const os = { + tmpdir: () => '/tmp', + networkInterfaces: () => { + throw new Error('no network interfaces for some reason') + }, + } + const defs = requireInject(defpath, { os }) + t.same(defs['local-address'].type, [null]) + t.end() + }) + t.end() +}) + +t.test('unicode allowed?', t => { + const { LC_ALL, LC_CTYPE, LANG } = process.env + t.teardown(() => Object.assign(process.env, { LC_ALL, LC_CTYPE, LANG })) + + process.env.LC_ALL = 'utf8' + process.env.LC_CTYPE = 'UTF-8' + process.env.LANG = 'Unicode utf-8' + + const lcAll = requireInject(defpath) + t.equal(lcAll.unicode.default, true) + process.env.LC_ALL = 'no unicode for youUUUU!' + const noLcAll = requireInject(defpath) + t.equal(noLcAll.unicode.default, false) + + delete process.env.LC_ALL + const lcCtype = requireInject(defpath) + t.equal(lcCtype.unicode.default, true) + process.env.LC_CTYPE = 'something other than unicode version 8' + const noLcCtype = requireInject(defpath) + t.equal(noLcCtype.unicode.default, false) + + delete process.env.LC_CTYPE + const lang = requireInject(defpath) + t.equal(lang.unicode.default, true) + process.env.LANG = 'ISO-8859-1' + const noLang = requireInject(defpath) + t.equal(noLang.unicode.default, false) + t.end() +}) + +t.test('cache', t => { + process.env.LOCALAPPDATA = 'app/data/local' + const defsWinLocalAppData = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsWinLocalAppData.cache.default, 'app/data/local/npm-cache') + + delete process.env.LOCALAPPDATA + const defsWinNoLocalAppData = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsWinNoLocalAppData.cache.default, '~/npm-cache') + + const defsNix = requireInject(defpath, { + [isWin]: false, + }) + t.equal(defsNix.cache.default, '~/.npm') + + const flat = {} + defsNix.cache.flatten('cache', { cache: '/some/cache/value' }, flat) + const {join} = require('path') + t.equal(flat.cache, join('/some/cache/value', '_cacache')) + + t.end() +}) + +t.test('flatteners that populate flat.omit array', t => { + t.test('also', t => { + const flat = {} + const obj = {} + + // ignored if setting is not dev or development + obj.also = 'ignored' + definitions.also.flatten('also', obj, flat) + t.strictSame(obj, {also: 'ignored'}, 'nothing done') + t.strictSame(flat, {}, 'nothing done') + + obj.also = 'development' + definitions.also.flatten('also', obj, flat) + t.strictSame(obj, { also: 'development', include: ['dev'] }, 'marked dev as included') + t.strictSame(flat, { omit: [] }, 'nothing omitted, so nothing changed') + + obj.omit = ['dev', 'optional'] + obj.include = [] + definitions.also.flatten('also', obj, flat) + t.strictSame(obj, { also: 'development', omit: ['dev', 'optional'], include: ['dev'] }, 'marked dev as included') + t.strictSame(flat, { omit: ['optional'] }, 'removed dev from omit') + t.end() + }) + + t.test('include', t => { + const flat = {} + const obj = { include: ['dev'] } + definitions.include.flatten('include', obj, flat) + t.strictSame(flat, {omit: []}, 'not omitting anything') + obj.omit = ['optional', 'dev'] + definitions.include.flatten('include', obj, flat) + t.strictSame(flat, {omit: ['optional']}, 'only omitting optional') + t.end() + }) + + t.test('omit', t => { + const flat = {} + const obj = { include: ['dev'], omit: ['dev', 'optional'] } + definitions.omit.flatten('omit', obj, flat) + t.strictSame(flat, { omit: ['optional'] }, 'do not omit what is included') + + process.env.NODE_ENV = 'production' + const defProdEnv = requireInject(defpath) + t.strictSame(defProdEnv.omit.default, ['dev'], 'omit dev in production') + t.end() + }) + + t.test('only', t => { + const flat = {} + const obj = { only: 'asdf' } + definitions.only.flatten('only', obj, flat) + t.strictSame(flat, {}, 'ignored if value is not production') + + obj.only = 'prod' + definitions.only.flatten('only', obj, flat) + t.strictSame(flat, {omit: ['dev']}, 'omit dev when --only=prod') + + obj.include = ['dev'] + flat.omit = [] + definitions.only.flatten('only', obj, flat) + t.strictSame(flat, {omit: []}, 'do not omit when included') + + t.end() + }) + + t.test('optional', t => { + const flat = {} + const obj = { optional: null } + + definitions.optional.flatten('optional', obj, flat) + t.strictSame(obj, { optional: null }, 'do nothing by default') + t.strictSame(flat, {}, 'do nothing by default') + + obj.optional = true + definitions.optional.flatten('optional', obj, flat) + t.strictSame(obj, {include: ['optional'], optional: true}, 'include optional when set') + t.strictSame(flat, {omit: []}, 'nothing to omit in flatOptions') + + delete obj.include + obj.optional = false + definitions.optional.flatten('optional', obj, flat) + t.strictSame(obj, {omit: ['optional'], optional: false}, 'omit optional when set false') + t.strictSame(flat, {omit: ['optional']}, 'omit optional when set false') + + t.end() + }) + + t.test('production', t => { + const flat = {} + const obj = {production: true} + definitions.production.flatten('production', obj, flat) + t.strictSame(obj, {production: true, omit: ['dev']}, '--production sets --omit=dev') + t.strictSame(flat, {omit: ['dev']}, '--production sets --omit=dev') + + delete obj.omit + obj.production = false + delete flat.omit + definitions.production.flatten('production', obj, flat) + t.strictSame(obj, {production: false}, '--no-production has no effect') + t.strictSame(flat, {}, '--no-production has no effect') + + obj.production = true + obj.include = ['dev'] + definitions.production.flatten('production', obj, flat) + t.strictSame(obj, {production: true, include: ['dev'], omit: ['dev']}, 'omit and include dev') + t.strictSame(flat, {omit: []}, 'do not omit dev when included') + + t.end() + }) + + t.end() +}) + +t.test('cache-max', t => { + const flat = {} + const obj = { 'cache-max': 10342 } + definitions['cache-max'].flatten('cache-max', obj, flat) + t.strictSame(flat, {}, 'no effect if not <= 0') + obj['cache-max'] = 0 + definitions['cache-max'].flatten('cache-max', obj, flat) + t.strictSame(flat, {preferOnline: true}, 'preferOnline if <= 0') + t.end() +}) + +t.test('cache-min', t => { + const flat = {} + const obj = { 'cache-min': 123 } + definitions['cache-min'].flatten('cache-min', obj, flat) + t.strictSame(flat, {}, 'no effect if not >= 9999') + obj['cache-min'] = 9999 + definitions['cache-min'].flatten('cache-min', obj, flat) + t.strictSame(flat, {preferOffline: true}, 'preferOffline if >=9999') + t.end() +}) + +t.test('color', t => { + const { isTTY } = process.stdout + t.teardown(() => process.stdout.isTTY = isTTY) + + const flat = {} + const obj = { color: 'always' } + + definitions.color.flatten('color', obj, flat) + t.strictSame(flat, {color: true}, 'true when --color=always') + + obj.color = false + definitions.color.flatten('color', obj, flat) + t.strictSame(flat, {color: false}, 'true when --no-color') + + process.stdout.isTTY = false + obj.color = true + definitions.color.flatten('color', obj, flat) + t.strictSame(flat, {color: false}, 'no color when stdout not tty') + process.stdout.isTTY = true + definitions.color.flatten('color', obj, flat) + t.strictSame(flat, {color: true}, '--color turns on color when stdout is tty') + + delete process.env.NO_COLOR + const defsAllowColor = requireInject(defpath) + t.equal(defsAllowColor.color.default, true, 'default true when no NO_COLOR env') + + process.env.NO_COLOR = '0' + const defsNoColor0 = requireInject(defpath) + t.equal(defsNoColor0.color.default, true, 'default true when no NO_COLOR=0') + + process.env.NO_COLOR = '1' + const defsNoColor1 = requireInject(defpath) + t.equal(defsNoColor1.color.default, false, 'default false when no NO_COLOR=1') + + t.end() +}) + +t.test('retry options', t => { + const obj = {} + // <config>: flat.retry[<option>] + const mapping = { + 'fetch-retries': 'retries', + 'fetch-retry-factor': 'factor', + 'fetch-retry-maxtimeout': 'maxTimeout', + 'fetch-retry-mintimeout': 'minTimeout', + } + for (const [config, option] of Object.entries(mapping)) { + const msg = `${config} -> retry.${option}` + const flat = {} + obj[config] = 99 + definitions[config].flatten(config, obj, flat) + t.strictSame(flat, {retry: {[option]: 99}}, msg) + delete obj[config] + } + t.end() +}) + +t.test('search options', t => { + const obj = {} + // <config>: flat.search[<option>] + const mapping = { + description: 'description', + searchexclude: 'exclude', + searchlimit: 'limit', + searchstaleness: 'staleness', + } + + for (const [config, option] of Object.entries(mapping)) { + const msg = `${config} -> search.${option}` + const flat = {} + obj[config] = 99 + definitions[config].flatten(config, obj, flat) + t.strictSame(flat, { search: { limit: 20, [option]: 99 }}, msg) + delete obj[config] + } + + const flat = {} + obj.searchopts = 'a=b&b=c' + definitions.searchopts.flatten('searchopts', obj, flat) + t.strictSame(flat, { + search: { + limit: 20, + opts: Object.assign(Object.create(null), { + a: 'b', + b: 'c', + }), + }, + }, 'searchopts -> querystring.parse() -> search.opts') + delete obj.searchopts + + t.end() +}) + +t.test('noProxy', t => { + const obj = { noproxy: ['1.2.3.4,2.3.4.5', '3.4.5.6'] } + const flat = {} + definitions.noproxy.flatten('noproxy', obj, flat) + t.strictSame(flat, { noProxy: '1.2.3.4,2.3.4.5,3.4.5.6' }) + t.end() +}) + +t.test('maxSockets', t => { + const obj = { maxsockets: 123 } + const flat = {} + definitions.maxsockets.flatten('maxsockets', obj, flat) + t.strictSame(flat, { maxSockets: 123 }) + t.end() +}) + +t.test('projectScope', t => { + const obj = { scope: 'asdf' } + const flat = {} + definitions.scope.flatten('scope', obj, flat) + t.strictSame(flat, { projectScope: '@asdf' }, 'prepend @ if needed') + + obj.scope = '@asdf' + definitions.scope.flatten('scope', obj, flat) + t.strictSame(flat, { projectScope: '@asdf' }, 'leave untouched if has @') + + t.end() +}) + +t.test('strictSSL', t => { + const obj = { 'strict-ssl': false } + const flat = {} + definitions['strict-ssl'].flatten('strict-ssl', obj, flat) + t.strictSame(flat, { strictSSL: false }) + obj['strict-ssl'] = true + definitions['strict-ssl'].flatten('strict-ssl', obj, flat) + t.strictSame(flat, { strictSSL: true }) + t.end() +}) + +t.test('shrinkwrap/package-lock', t => { + const obj = { shrinkwrap: false } + const flat = {} + definitions.shrinkwrap.flatten('shrinkwrap', obj, flat) + t.strictSame(flat, {packageLock: false}) + obj.shrinkwrap = true + definitions.shrinkwrap.flatten('shrinkwrap', obj, flat) + t.strictSame(flat, {packageLock: true}) + + delete obj.shrinkwrap + obj['package-lock'] = false + definitions['package-lock'].flatten('package-lock', obj, flat) + t.strictSame(flat, {packageLock: false}) + obj['package-lock'] = true + definitions['package-lock'].flatten('package-lock', obj, flat) + t.strictSame(flat, {packageLock: true}) + + t.end() +}) + +t.test('scriptShell', t => { + const obj = { 'script-shell': null } + const flat = {} + definitions['script-shell'].flatten('script-shell', obj, flat) + t.ok(Object.prototype.hasOwnProperty.call(flat, 'scriptShell'), + 'should set it to undefined explicitly') + t.strictSame(flat, { scriptShell: undefined }, 'no other fields') + + obj['script-shell'] = 'asdf' + definitions['script-shell'].flatten('script-shell', obj, flat) + t.strictSame(flat, { scriptShell: 'asdf' }, 'sets if not falsey') + + t.end() +}) + +t.test('defaultTag', t => { + const obj = { tag: 'next' } + const flat = {} + definitions.tag.flatten('tag', obj, flat) + t.strictSame(flat, {defaultTag: 'next'}) + t.end() +}) + +t.test('timeout', t => { + const obj = { 'fetch-timeout': 123 } + const flat = {} + definitions['fetch-timeout'].flatten('fetch-timeout', obj, flat) + t.strictSame(flat, {timeout: 123}) + t.end() +}) + +t.test('saveType', t => { + t.test('save-prod', t => { + const obj = { 'save-prod': false } + const flat = {} + definitions['save-prod'].flatten('save-prod', obj, flat) + t.strictSame(flat, {}, 'no effect if false and missing') + flat.saveType = 'prod' + definitions['save-prod'].flatten('save-prod', obj, flat) + t.strictSame(flat, {}, 'remove if false and set to prod') + flat.saveType = 'dev' + definitions['save-prod'].flatten('save-prod', obj, flat) + t.strictSame(flat, {saveType: 'dev'}, 'ignore if false and not already prod') + obj['save-prod'] = true + definitions['save-prod'].flatten('save-prod', obj, flat) + t.strictSame(flat, {saveType: 'prod'}, 'set to prod if true') + t.end() + }) + + t.test('save-dev', t => { + const obj = { 'save-dev': false } + const flat = {} + definitions['save-dev'].flatten('save-dev', obj, flat) + t.strictSame(flat, {}, 'no effect if false and missing') + flat.saveType = 'dev' + definitions['save-dev'].flatten('save-dev', obj, flat) + t.strictSame(flat, {}, 'remove if false and set to dev') + flat.saveType = 'prod' + obj['save-dev'] = false + definitions['save-dev'].flatten('save-dev', obj, flat) + t.strictSame(flat, {saveType: 'prod'}, 'ignore if false and not already dev') + obj['save-dev'] = true + definitions['save-dev'].flatten('save-dev', obj, flat) + t.strictSame(flat, {saveType: 'dev'}, 'set to dev if true') + t.end() + }) + + t.test('save-bundle', t => { + const obj = { 'save-bundle': true } + const flat = {} + definitions['save-bundle'].flatten('save-bundle', obj, flat) + t.strictSame(flat, {saveBundle: true}, 'set the saveBundle flag') + + obj['save-bundle'] = false + definitions['save-bundle'].flatten('save-bundle', obj, flat) + t.strictSame(flat, {saveBundle: false}, 'unset the saveBundle flag') + + obj['save-bundle'] = true + obj['save-peer'] = true + definitions['save-bundle'].flatten('save-bundle', obj, flat) + t.strictSame(flat, {saveBundle: false}, 'false if save-peer is set') + + t.end() + }) + + t.test('save-peer', t => { + const obj = { 'save-peer': false} + const flat = {} + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {}, 'no effect if false and not yet set') + + obj['save-peer'] = true + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {saveType: 'peer'}, 'set saveType to peer if unset') + + flat.saveType = 'optional' + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {saveType: 'peerOptional'}, 'set to peerOptional if optional already') + + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {saveType: 'peerOptional'}, 'no effect if already peerOptional') + + obj['save-peer'] = false + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {saveType: 'optional'}, 'switch peerOptional to optional if false') + + obj['save-peer'] = false + flat.saveType = 'peer' + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {}, 'remove saveType if peer and setting false') + + t.end() + }) + + t.test('save-optional', t => { + const obj = { 'save-optional': false} + const flat = {} + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {}, 'no effect if false and not yet set') + + obj['save-optional'] = true + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {saveType: 'optional'}, 'set saveType to optional if unset') + + flat.saveType = 'peer' + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {saveType: 'peerOptional'}, 'set to peerOptional if peer already') + + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {saveType: 'peerOptional'}, 'no effect if already peerOptional') + + obj['save-optional'] = false + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {saveType: 'peer'}, 'switch peerOptional to peer if false') + + flat.saveType = 'optional' + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {}, 'remove saveType if optional and setting false') + + t.end() + }) + + t.end() +}) + +t.test('cafile -> flat.ca', t => { + const path = t.testdir({ + cafile: ` +-----BEGIN CERTIFICATE----- +XXXX +XXXX +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +YYYY\r +YYYY\r +-----END CERTIFICATE----- +`, + }) + const cafile = resolve(path, 'cafile') + + const obj = {} + const flat = {} + definitions.cafile.flatten('cafile', obj, flat) + t.strictSame(flat, {}, 'no effect if no cafile set') + obj.cafile = resolve(path, 'no/cafile/here') + definitions.cafile.flatten('cafile', obj, flat) + t.strictSame(flat, {}, 'no effect if cafile not found') + obj.cafile = cafile + definitions.cafile.flatten('cafile', obj, flat) + t.strictSame(flat, { + ca: [ + '-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----', + '-----BEGIN CERTIFICATE-----\nYYYY\nYYYY\n-----END CERTIFICATE-----', + ], + }) + t.test('error other than ENOENT gets thrown', t => { + const poo = new Error('poo') + const defnReadFileThrows = requireInject(defpath, { + fs: { + ...require('fs'), + readFileSync: () => { + throw poo + }, + }, + }) + t.throws(() => defnReadFileThrows.cafile.flatten('cafile', obj, {}), poo) + t.end() + }) + + t.end() +}) + +t.test('detect CI', t => { + const defnNoCI = requireInject(defpath, { + '@npmcli/ci-detect': () => false, + }) + const defnCIFoo = requireInject(defpath, { + '@npmcli/ci-detect': () => 'foo', + }) + t.equal(defnNoCI['ci-name'].default, null, 'null when not in CI') + t.equal(defnCIFoo['ci-name'].default, 'foo', 'name of CI when in CI') + t.end() +}) + +t.test('user-agent', t => { + const obj = { + 'user-agent': definitions['user-agent'].default, + 'npm-version': '1.2.3', + 'node-version': '9.8.7', + } + const flat = {} + const expectNoCI = `npm/1.2.3 node/9.8.7 ` + + `${process.platform} ${process.arch}` + definitions['user-agent'].flatten('user-agent', obj, flat) + t.equal(flat.userAgent, expectNoCI) + obj['ci-name'] = 'foo' + const expectCI = `${expectNoCI} ci/foo` + definitions['user-agent'].flatten('user-agent', obj, flat) + t.equal(flat.userAgent, expectCI) + t.end() +}) diff --git a/deps/npm/test/lib/utils/config/describe-all.js b/deps/npm/test/lib/utils/config/describe-all.js new file mode 100644 index 0000000000..814d92ac95 --- /dev/null +++ b/deps/npm/test/lib/utils/config/describe-all.js @@ -0,0 +1,6 @@ +const t = require('tap') +const describeAll = require('../../../../lib/utils/config/describe-all.js') +// this basically ends up being a duplicate of the helpdoc dumped into +// a snapshot, but it verifies that we get the same help output on every +// platform where we run CI. +t.matchSnapshot(describeAll()) diff --git a/deps/npm/test/lib/utils/config/flatten.js b/deps/npm/test/lib/utils/config/flatten.js new file mode 100644 index 0000000000..9fac0820cb --- /dev/null +++ b/deps/npm/test/lib/utils/config/flatten.js @@ -0,0 +1,34 @@ +const t = require('tap') +const flatten = require('../../../../lib/utils/config/flatten.js') + +require.main.filename = '/path/to/npm' +delete process.env.NODE +process.execPath = '/path/to/node' + +const obj = { + 'save-dev': true, + '@foobar:registry': 'https://foo.bar.com/', + '//foo.bar.com:_authToken': 'foobarbazquuxasdf', + userconfig: '/path/to/.npmrc', +} + +const flat = flatten(obj) +t.strictSame(flat, { + saveType: 'dev', + '@foobar:registry': 'https://foo.bar.com/', + '//foo.bar.com:_authToken': 'foobarbazquuxasdf', + npmBin: '/path/to/npm', + nodeBin: '/path/to/node', + hashAlgorithm: 'sha1', +}) + +// now flatten something else on top of it. +process.env.NODE = '/usr/local/bin/node.exe' +flatten({ 'save-dev': false }, flat) +t.strictSame(flat, { + '@foobar:registry': 'https://foo.bar.com/', + '//foo.bar.com:_authToken': 'foobarbazquuxasdf', + npmBin: '/path/to/npm', + nodeBin: '/usr/local/bin/node.exe', + hashAlgorithm: 'sha1', +}) diff --git a/deps/npm/test/lib/utils/config/index.js b/deps/npm/test/lib/utils/config/index.js new file mode 100644 index 0000000000..75d72e784f --- /dev/null +++ b/deps/npm/test/lib/utils/config/index.js @@ -0,0 +1,24 @@ +const t = require('tap') +const config = require('../../../../lib/utils/config/index.js') +const flatten = require('../../../../lib/utils/config/flatten.js') +const definitions = require('../../../../lib/utils/config/definitions.js') +const describeAll = require('../../../../lib/utils/config/describe-all.js') +t.matchSnapshot(config.shorthands, 'shorthands') + +// just spot check a few of these to show that we got defaults assembled +t.match(config.defaults, { + registry: definitions.registry.default, + 'init-module': definitions['init-module'].default, +}) + +// is a getter, so changes are reflected +definitions.registry.default = 'https://example.com' +t.strictSame(config.defaults.registry, 'https://example.com') + +t.strictSame(config, { + defaults: config.defaults, + shorthands: config.shorthands, + flatten, + definitions, + describeAll, +}) diff --git a/deps/npm/test/lib/utils/did-you-mean.js b/deps/npm/test/lib/utils/did-you-mean.js index 0c9c95c7f9..68893a800f 100644 --- a/deps/npm/test/lib/utils/did-you-mean.js +++ b/deps/npm/test/lib/utils/did-you-mean.js @@ -1,7 +1,39 @@ const t = require('tap') +const requireInject = require('require-inject') +const npm = requireInject('../../../lib/npm.js') + const dym = require('../../../lib/utils/did-you-mean.js') -t.equal(dym('asdfa', ['asdf', 'asfd', 'adfs', 'safd', 'foobarbaz', 'foobar']), - '\nDid you mean this?\n asdf') -t.equal(dym('asdfa', ['asdf', 'sdfa', 'foo', 'bar', 'fdsa']), - '\nDid you mean one of these?\n asdf\n sdfa') -t.equal(dym('asdfa', ['install', 'list', 'test']), '') +t.test('did-you-mean', t => { + npm.load(err => { + t.notOk(err) + t.test('nistall', async t => { + const result = await dym(npm, npm.localPrefix, 'nistall') + t.match(result, 'npm install') + }) + t.test('sttest', async t => { + const result = await dym(npm, npm.localPrefix, 'sttest') + t.match(result, 'npm test') + t.match(result, 'npm run posttest') + }) + t.test('npz', async t => { + const result = await dym(npm, npm.localPrefix, 'npxx') + t.match(result, 'npm exec npx') + }) + t.test('qwuijbo', async t => { + const result = await dym(npm, npm.localPrefix, 'qwuijbo') + t.match(result, '') + }) + t.end() + }) +}) + +t.test('missing bin and script properties', async t => { + const path = t.testdir({ + 'package.json': JSON.stringify({ + name: 'missing-bin', + }), + }) + + const result = await dym(npm, path, 'nistall') + t.match(result, 'npm install') +}) diff --git a/deps/npm/test/lib/utils/flat-options.js b/deps/npm/test/lib/utils/flat-options.js deleted file mode 100644 index 6f580fabc4..0000000000 --- a/deps/npm/test/lib/utils/flat-options.js +++ /dev/null @@ -1,359 +0,0 @@ -const t = require('tap') - -process.env.NODE = '/path/to/some/node' -process.env.NODE_ENV = 'development' - -const logs = [] -const log = require('npmlog') -log.warn = (...args) => logs.push(['warn', ...args]) -log.verbose = (...args) => logs.push(['verbose', ...args]) - -class Mocknpm { - constructor (opts = {}) { - this.modes = { - exec: 0o777, - file: 0o666, - umask: 0o22, - } - this.color = true - this.projectScope = '@npmcli' - this.tmp = '/tmp' - this.command = null - this.globalPrefix = '/usr/local' - this.localPrefix = '/path/to/npm/cli' - this.prefix = this.localPrefix - this.version = '7.6.5' - this.config = new MockConfig(opts) - this.flatOptions = null - } -} - -class MockConfig { - constructor (opts = {}) { - this.list = [{ - cache: 'cache', - 'node-version': '1.2.3', - global: 'global', - registry: 'registry', - access: 'access', - 'always-auth': 'always-auth', - audit: 'audit', - 'audit-level': 'audit-level', - 'auth-type': 'auth-type', - before: 'before', - browser: 'browser', - ca: 'ca', - cafile: 'cafile', - call: 'call', - cert: 'cert', - key: 'key', - 'cache-lock-retries': 'cache-lock-retries', - 'cache-lock-stale': 'cache-lock-stale', - 'cache-lock-wait': 'cache-lock-wait', - cidr: 'cidr', - 'read-only': 'read-only', - preid: 'preid', - 'tag-version-prefix': 'tag-version-prefix', - 'allow-same-version': 'allow-same-version', - message: 'message', - 'commit-hooks': 'commit-hooks', - 'git-tag-version': 'git-tag-version', - 'sign-git-commit': 'sign-git-commit', - 'sign-git-tag': 'sign-git-tag', - depth: 'depth', - description: 'description', - searchexclude: 'searchexclude', - searchlimit: 'searchlimit', - searchopts: 'from=1', - searchstaleness: 'searchstaleness', - 'dry-run': 'dry-run', - 'engine-strict': 'engine-strict', - 'fetch-retries': 'fetch-retries', - 'fetch-retry-factor': 'fetch-retry-factor', - 'fetch-retry-mintimeout': 'fetch-retry-mintimeout', - 'fetch-retry-maxtimeout': 'fetch-retry-maxtimeout', - 'fetch-timeout': 'fetch-timeout', - force: 'force', - 'format-package-lock': 'format-package-lock', - fund: 'fund', - git: 'git', - viewer: 'viewer', - editor: 'editor', - 'bin-links': 'bin-links', - 'rebuild-bundle': 'rebuild-bundle', - package: 'package', - 'package-lock': 'package-lock', - 'package-lock-only': 'package-lock-only', - 'global-style': 'global-style', - 'legacy-bundling': 'legacy-bundling', - 'script-shell': 'script-shell', - omit: [], - include: [], - save: 'save', - 'save-bundle': 'save-bundle', - 'save-dev': 'save-dev', - 'save-optional': 'save-optional', - 'save-peer': 'save-peer', - 'save-prod': 'save-prod', - 'save-exact': 'save-exact', - 'save-prefix': 'save-prefix', - otp: 'otp', - offline: 'offline', - 'prefer-online': 'prefer-online', - 'prefer-offline': 'prefer-offline', - 'cache-max': 'cache-max', - 'cache-min': 'cache-min', - 'strict-ssl': 'strict-ssl', - scope: '', - tag: 'tag', - 'user-agent': 'user-agent', - '@scope:registry': '@scope:registry', - '//nerf.dart:_authToken': '//nerf.dart:_authToken', - proxy: 'proxy', - noproxy: 'noproxy', - ...opts, - }] - } - - get (key) { - return this.list[0][key] - } - - set (key, val) { - this.list[0][key] = val - } -} - -const flatOptions = require('../../../lib/utils/flat-options.js') -t.match(logs, [[ - 'verbose', - 'npm-session', - /^[0-9a-f]{16}$/, -]], 'logged npm session verbosely') -logs.length = 0 - -t.test('basic', t => { - const npm = new Mocknpm() - const generatedFlat = flatOptions(npm) - const clean = { - ...generatedFlat, - npmBin: '/path/to/npm/bin.js', - log: {}, - npmSession: '12345', - cache: generatedFlat.cache.replace(/\\/g, '/'), - } - t.matchSnapshot(clean, 'flat options') - t.equal(generatedFlat.npmCommand, null, 'command not set yet') - npm.command = 'view' - t.equal(generatedFlat.npmCommand, 'view', 'command updated via getter') - t.equal(generatedFlat.npmBin, require.main.filename) - // test the object is frozen - generatedFlat.newField = 'asdf' - t.equal(generatedFlat.newField, undefined, 'object is frozen') - const preExistingOpts = { flat: 'options' } - npm.flatOptions = preExistingOpts - t.equal(flatOptions(npm), preExistingOpts, 'use pre-existing npm.flatOptions') - t.end() -}) - -t.test('get preferOffline from cache-min', t => { - const npm = new Mocknpm({ - 'cache-min': 9999999, - 'prefer-offline': undefined, - }) - const opts = flatOptions(npm) - t.equal(opts.preferOffline, true, 'got preferOffline from cache min') - logs.length = 0 - t.equal(opts.cacheMin, undefined, 'opts.cacheMin is not set') - t.match(logs, []) - logs.length = 0 - t.end() -}) - -t.test('get preferOnline from cache-max', t => { - const npm = new Mocknpm({ - 'cache-max': -1, - 'prefer-online': undefined, - }) - const opts = flatOptions(npm) - t.equal(opts.preferOnline, true, 'got preferOnline from cache min') - logs.length = 0 - t.equal(opts.cacheMax, undefined, 'opts.cacheMax is not set') - t.match(logs, []) - logs.length = 0 - t.end() -}) - -t.test('tag emits warning', t => { - const npm = new Mocknpm({ tag: 'foobar' }) - t.equal(flatOptions(npm).tag, 'foobar', 'tag is foobar') - t.match(logs, []) - logs.length = 0 - t.end() -}) - -t.test('omit/include options', t => { - t.test('omit explicitly', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: ['dev', 'optional', 'peer'], - }) - t.strictSame(flatOptions(npm).omit, ['dev', 'optional', 'peer']) - t.equal(process.env.NODE_ENV, 'production') - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('omit and include some', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: ['dev', 'optional', 'peer'], - include: ['peer'], - }) - t.strictSame(flatOptions(npm).omit, ['dev', 'optional']) - t.equal(process.env.NODE_ENV, 'production') - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('dev flag', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: ['dev', 'optional', 'peer'], - include: [], - dev: true, - }) - t.strictSame(flatOptions(npm).omit, ['optional', 'peer']) - t.equal(process.env.NODE_ENV, NODE_ENV) - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('production flag', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: [], - include: [], - production: true, - }) - t.strictSame(flatOptions(npm).omit, ['dev']) - t.equal(process.env.NODE_ENV, 'production') - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('only', t => { - const { NODE_ENV } = process.env - const cases = ['prod', 'production'] - t.plan(cases.length) - cases.forEach(c => t.test(c, t => { - const npm = new Mocknpm({ - omit: [], - include: [], - only: c, - }) - t.strictSame(flatOptions(npm).omit, ['dev']) - t.equal(process.env.NODE_ENV, 'production') - process.env.NODE_ENV = NODE_ENV - t.end() - })) - }) - - t.test('also dev', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: ['dev', 'optional', 'peer'], - also: 'dev', - }) - t.strictSame(flatOptions(npm).omit, ['optional', 'peer']) - t.equal(process.env.NODE_ENV, NODE_ENV) - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('no-optional', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - optional: false, - omit: null, - include: null, - }) - t.strictSame(flatOptions(npm).omit, ['optional']) - t.equal(process.env.NODE_ENV, NODE_ENV) - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.end() -}) - -t.test('get the node without the environ', t => { - delete process.env.NODE - t.equal(flatOptions(new Mocknpm()).nodeBin, process.execPath) - t.end() -}) - -t.test('various default values and falsey fallbacks', t => { - const npm = new Mocknpm({ - 'script-shell': false, - registry: 'http://example.com', - searchlimit: 0, - 'save-exact': false, - 'save-prefix': '>=', - }) - const opts = flatOptions(npm) - t.equal(opts.scriptShell, undefined, 'scriptShell is undefined if falsey') - t.equal(opts.search.limit, 20, 'searchLimit defaults to 20') - t.equal(opts.savePrefix, '>=', 'save-prefix respected if no save-exact') - t.equal(opts.scope, '', 'scope defaults to empty string') - logs.length = 0 - t.end() -}) - -t.test('legacy _auth token', t => { - const npm = new Mocknpm({ - _auth: 'asdfasdf', - }) - t.strictSame( - flatOptions(npm)._auth, - 'asdfasdf', - 'should set legacy _auth token' - ) - t.end() -}) - -t.test('save-type', t => { - const base = { - 'save-optional': false, - 'save-peer': false, - 'save-dev': false, - 'save-prod': false, - } - const cases = [ - ['peerOptional', { - 'save-optional': true, - 'save-peer': true, - }], - ['optional', { - 'save-optional': true, - }], - ['dev', { - 'save-dev': true, - }], - ['peer', { - 'save-peer': true, - }], - ['prod', { - 'save-prod': true, - }], - [null, {}], - ] - for (const [expect, options] of cases) { - const opts = flatOptions(new Mocknpm({ - ...base, - ...options, - })) - t.equal(opts.saveType, expect, JSON.stringify(options)) - } - t.end() -}) diff --git a/deps/npm/test/lib/utils/lifecycle-cmd.js b/deps/npm/test/lib/utils/lifecycle-cmd.js index 3e3a7da434..862c87a8e0 100644 --- a/deps/npm/test/lib/utils/lifecycle-cmd.js +++ b/deps/npm/test/lib/utils/lifecycle-cmd.js @@ -10,6 +10,7 @@ const npm = { }, } t.test('create a lifecycle command', t => { + t.plan(5) class TestStage extends LifecycleCmd { static get name () { return 'test-stage' @@ -20,6 +21,9 @@ t.test('create a lifecycle command', t => { cmd.exec(['some', 'args'], (er, result) => { t.same(runArgs, ['test-stage', 'some', 'args']) t.strictSame(result, 'called npm.commands.run') - t.end() + }) + cmd.execWorkspaces(['some', 'args'], [], (er, result) => { + t.same(runArgs, ['test-stage', 'some', 'args']) + t.strictSame(result, 'called npm.commands.run') }) }) diff --git a/deps/npm/test/lib/utils/npm-usage.js b/deps/npm/test/lib/utils/npm-usage.js index fbc453811e..ebf637ae1c 100644 --- a/deps/npm/test/lib/utils/npm-usage.js +++ b/deps/npm/test/lib/utils/npm-usage.js @@ -1,12 +1,5 @@ const t = require('tap') - -const OUTPUT = [] -const output = (...msg) => OUTPUT.push(msg) -const requireInject = require('require-inject') -const usage = require('../../../lib/utils/npm-usage.js') - -const npm = requireInject('../../../lib/npm.js') -npm.output = output +const npm = require('../../../lib/npm.js') t.test('usage', t => { t.afterEach((cb) => { @@ -29,61 +22,19 @@ t.test('usage', t => { npm.config.set('userconfig', '/some/config/file/.npmrc') t.test('basic usage', t => { - usage(npm) - t.equal(OUTPUT.length, 1) - t.equal(OUTPUT[0].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - OUTPUT.length = 0 + t.matchSnapshot(npm.usage) t.end() }) t.test('with browser', t => { npm.config.set('viewer', 'browser') - usage(npm) - t.equal(OUTPUT.length, 1) - t.equal(OUTPUT[0].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - OUTPUT.length = 0 - npm.config.set('viewer', null) + t.matchSnapshot(npm.usage) t.end() }) t.test('with long', t => { npm.config.set('long', true) - usage(npm) - t.equal(OUTPUT.length, 1) - t.equal(OUTPUT[0].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - OUTPUT.length = 0 - npm.config.set('long', false) - t.end() - }) - - t.test('did you mean?', t => { - npm.argv.push('unistnall') - usage(npm) - t.equal(OUTPUT.length, 2) - t.equal(OUTPUT[0].length, 1) - t.equal(OUTPUT[1].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - t.matchSnapshot(OUTPUT[1][0]) - OUTPUT.length = 0 - npm.argv.length = 0 - t.end() - }) - - t.test('did you mean?', t => { - npm.argv.push('unistnall') - const { exitCode } = process - t.teardown(() => { - if (t.passing()) - process.exitCode = exitCode - }) - // make sure it fails when invalid - usage(npm, false) - t.equal(process.exitCode, 1) - OUTPUT.length = 0 - npm.argv.length = 0 + t.matchSnapshot(npm.usage) t.end() }) @@ -106,11 +57,7 @@ t.test('usage', t => { configurable: true, writable: true, }) - usage(npm) - t.equal(OUTPUT.length, 1) - t.equal(OUTPUT[0].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - OUTPUT.length = 0 + t.matchSnapshot(npm.usage) t.end() }) } diff --git a/deps/npm/test/lib/utils/read-local-package.js b/deps/npm/test/lib/utils/read-local-package.js index 9ae21f7d62..4b693afb48 100644 --- a/deps/npm/test/lib/utils/read-local-package.js +++ b/deps/npm/test/lib/utils/read-local-package.js @@ -1,22 +1,17 @@ const requireInject = require('require-inject') const { test } = require('tap') +const mockNpm = require('../../fixtures/mock-npm') -let prefix -const _flatOptions = { +const config = { json: false, global: false, - get prefix () { - return prefix - }, } +const npm = mockNpm({ config }) const readLocalPackageName = requireInject('../../../lib/utils/read-local-package.js') -const npm = { - flatOptions: _flatOptions, -} test('read local package.json', async (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'my-local-package', version: '1.0.0', @@ -31,7 +26,7 @@ test('read local package.json', async (t) => { }) test('read local scoped-package.json', async (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: '@my-scope/my-local-package', version: '1.0.0', @@ -46,13 +41,13 @@ test('read local scoped-package.json', async (t) => { }) test('read using --global', async (t) => { - prefix = t.testdir({}) - _flatOptions.global = true + npm.prefix = t.testdir({}) + config.global = true const packageName = await readLocalPackageName(npm) t.equal( packageName, undefined, 'should not retrieve a package name' ) - _flatOptions.global = false + config.global = false }) diff --git a/deps/npm/test/lib/utils/tar.js b/deps/npm/test/lib/utils/tar.js index b780a73e5e..d9b8c5584a 100644 --- a/deps/npm/test/lib/utils/tar.js +++ b/deps/npm/test/lib/utils/tar.js @@ -101,9 +101,9 @@ test('should getContents of a tarball', async (t) => { id: 'my-cool-pkg@1.0.0', name: 'my-cool-pkg', version: '1.0.0', - size: 149, + size: 146, unpackedSize: 49, - shasum: 'c0bfd67a5142104e429afda09119eedd6a30d2fc', + shasum: 'b8379c5e69693cdda73aec3d81dae1d11c1e75bd', integrity: ssri.parse(integrity.sha512[0]), filename: 'my-cool-pkg-1.0.0.tgz', files: [{ path: 'package.json', size: 49, mode: 420 }], diff --git a/deps/npm/test/lib/version.js b/deps/npm/test/lib/version.js index a8fcd831fb..35d3d92cd2 100644 --- a/deps/npm/test/lib/version.js +++ b/deps/npm/test/lib/version.js @@ -1,21 +1,23 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let result = [] const noop = () => null -const npm = { - flatOptions: { - tagVersionPrefix: 'v', - json: false, - }, +const config = { + 'tag-version-prefix': 'v', + json: false, +} +const npm = mockNpm({ + config, prefix: '', version: '1.0.0', output: (...msg) => { for (const m of msg) result.push(m) }, -} +}) const mocks = { libnpmversion: noop, } @@ -25,7 +27,7 @@ const version = new Version(npm) const _processVersions = process.versions t.afterEach(cb => { - npm.flatOptions.json = false + config.json = false npm.prefix = '' process.versions = _processVersions result = [] @@ -116,7 +118,7 @@ t.test('failure reading package.json', t => { t.test('--json option', t => { const prefix = t.testdir({}) - npm.flatOptions.json = true + config.json = true npm.prefix = prefix Object.defineProperty(process, 'versions', { value: {} }) @@ -140,8 +142,6 @@ t.test('with one arg', t => { t.deepEqual( opts, { - tagVersionPrefix: 'v', - json: false, path: '', }, 'should forward expected options' diff --git a/deps/npm/test/lib/view.js b/deps/npm/test/lib/view.js index 1363a5b9f9..d136a1f418 100644 --- a/deps/npm/test/lib/view.js +++ b/deps/npm/test/lib/view.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let logs const cleanLogs = (done) => { @@ -243,34 +244,33 @@ t.test('should log package info', t => { packument, }, }) - const view = new View({ - flatOptions: { - global: false, - }, + const npm = mockNpm({ + config: { global: false }, }) + const view = new View(npm) const ViewJson = requireInject('../../lib/view.js', { pacote: { packument, }, }) - const viewJson = new ViewJson({ - flatOptions: { - json: true, - }, + const jsonNpm = mockNpm({ + config: { json: true }, }) + const viewJson = new ViewJson(jsonNpm) const ViewUnicode = requireInject('../../lib/view.js', { pacote: { packument, }, }) - const viewUnicode = new ViewUnicode({ - flatOptions: { + const unicodeNpm = mockNpm({ + config: { global: false, unicode: true, }, }) + const viewUnicode = new ViewUnicode(unicodeNpm) t.test('package with license, bugs, repository and other fields', t => { view.exec(['green@1.0.0'], () => { @@ -351,13 +351,14 @@ t.test('should log info of package in current working dir', t => { packument, }, }) - const view = new View({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { - defaultTag: '1.0.0', + config: { + tag: '1.0.0', global: false, }, }) + const view = new View(npm) t.test('specific version', t => { view.exec(['.@1.0.0'], () => { @@ -382,23 +383,24 @@ t.test('should log info by field name', t => { packument, }, }) - const viewJson = new ViewJson({ - flatOptions: { + const jsonNpm = mockNpm({ + config: { json: true, global: false, }, }) + const viewJson = new ViewJson(jsonNpm) + const View = requireInject('../../lib/view.js', { pacote: { packument, }, }) - const view = new View({ - flatOptions: { - global: false, - }, + const npm = mockNpm({ + config: { global: false }, }) + const view = new View(npm) t.test('readme', t => { view.exec(['yellow@1.0.0', 'readme'], () => { @@ -468,11 +470,10 @@ t.test('should log info by field name', t => { t.test('throw error if global mode', (t) => { const View = requireInject('../../lib/view.js') - const view = new View({ - flatOptions: { - global: true, - }, + const npm = mockNpm({ + config: { global: true }, }) + const view = new View(npm) view.exec([], (err) => { t.equals(err.message, 'Cannot use view command in global mode.') t.end() @@ -483,12 +484,11 @@ t.test('throw ENOENT error if package.json misisng', (t) => { const testDir = t.testdir({}) const View = requireInject('../../lib/view.js') - const view = new View({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { - global: false, - }, + config: { global: false }, }) + const view = new View(npm) view.exec([], (err) => { t.match(err, { code: 'ENOENT' }) t.end() @@ -501,12 +501,11 @@ t.test('throw EJSONPARSE error if package.json not json', (t) => { }) const View = requireInject('../../lib/view.js') - const view = new View({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { - global: false, - }, + config: { global: false }, }) + const view = new View(npm) view.exec([], (err) => { t.match(err, { code: 'EJSONPARSE' }) t.end() @@ -519,12 +518,11 @@ t.test('throw error if package.json has no name', (t) => { }) const View = requireInject('../../lib/view.js') - const view = new View({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { - global: false, - }, + config: { global: false }, }) + const view = new View(npm) view.exec([], (err) => { t.equals(err.message, 'Invalid package.json, no "name" field') t.end() @@ -537,12 +535,13 @@ t.test('throws when unpublished', (t) => { packument, }, }) - const view = new View({ - flatOptions: { - defaultTag: '1.0.1', + const npm = mockNpm({ + config: { + tag: '1.0.1', global: false, }, }) + const view = new View(npm) view.exec(['red'], (err) => { t.equals(err.code, 'E404') t.end() @@ -555,12 +554,13 @@ t.test('completion', async t => { packument, }, }) - const view = new View({ - flatOptions: { - defaultTag: '1.0.1', + const npm = mockNpm({ + config: { + tag: '1.0.1', global: false, }, }) + const view = new View(npm) const res = await view.completion({ conf: { argv: { remain: ['npm', 'view', 'green@1.0.0'] } }, }) @@ -570,11 +570,13 @@ t.test('completion', async t => { t.test('no registry completion', async t => { const View = requireInject('../../lib/view.js') - const view = new View({ - flatOptions: { - defaultTag: '1.0.1', + const npm = mockNpm({ + config: { + tag: '1.0.1', + global: false, }, }) + const view = new View(npm) const res = await view.completion({conf: { argv: { remain: ['npm', 'view'] } } }) t.notOk(res, 'there is no package completion') t.end() diff --git a/deps/npm/test/lib/whoami.js b/deps/npm/test/lib/whoami.js index 1a1ecd2574..b242ea8941 100644 --- a/deps/npm/test/lib/whoami.js +++ b/deps/npm/test/lib/whoami.js @@ -1,18 +1,21 @@ const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('whoami', (t) => { t.plan(3) const Whoami = requireInject('../../lib/whoami.js', { '../../lib/utils/get-identity.js': () => Promise.resolve('foo'), }) - const whoami = new Whoami({ - flatOptions: {}, + const npm = mockNpm({ + config: { json: false }, output: (output) => { t.equal(output, 'foo', 'should output the username') }, }) + const whoami = new Whoami(npm) + whoami.exec([], (err) => { t.ifError(err, 'npm whoami') t.ok('should successfully print username') @@ -24,12 +27,13 @@ test('whoami json', (t) => { const Whoami = requireInject('../../lib/whoami.js', { '../../lib/utils/get-identity.js': () => Promise.resolve('foo'), }) - const whoami = new Whoami({ - flatOptions: { json: true }, + const npm = mockNpm({ + config: { json: true }, output: (output) => { - t.equal(output, '"foo"', 'should output the username as json') + t.equal(output, '"foo"', 'should output the username') }, }) + const whoami = new Whoami(npm) whoami.exec([], (err) => { t.ifError(err, 'npm whoami') diff --git a/deps/npm/test/lib/workspaces/get-workspaces.js b/deps/npm/test/lib/workspaces/get-workspaces.js new file mode 100644 index 0000000000..ebed9dd35c --- /dev/null +++ b/deps/npm/test/lib/workspaces/get-workspaces.js @@ -0,0 +1,199 @@ +const { resolve } = require('path') +const t = require('tap') +const getWorkspaces = require('../../../lib/workspaces/get-workspaces.js') + +const normalizePath = p => p + .replace(/\\+/g, '/') + .replace(/\r\n/g, '\n') + +const cleanOutput = (str, path) => normalizePath(str) + .replace(normalizePath(path), '{PATH}') + +const clean = (res, path) => { + const cleaned = new Map() + for (const [key, value] of res.entries()) + cleaned.set(key, cleanOutput(value, path)) + return cleaned +} + +t.test('get-workspaces', async t => { + const path = t.testdir({ + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + scripts: { glorp: 'echo a doing the glerp glop' }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '2.0.0', + scripts: { glorp: 'echo b doing the glerp glop' }, + }), + }, + c: { + 'package.json': JSON.stringify({ + name: 'c', + version: '1.0.0', + scripts: { + test: 'exit 0', + posttest: 'echo posttest', + lorem: 'echo c lorem', + }, + }), + }, + d: { + 'package.json': JSON.stringify({ + name: 'd', + version: '1.0.0', + scripts: { + test: 'exit 0', + posttest: 'echo posttest', + }, + }), + }, + e: { + 'package.json': JSON.stringify({ + name: 'e', + scripts: { test: 'exit 0', start: 'echo start something' }, + }), + }, + noscripts: { + 'package.json': JSON.stringify({ + name: 'noscripts', + version: '1.0.0', + }), + }, + }, + 'package.json': JSON.stringify({ + name: 'x', + version: '1.2.3', + workspaces: ['packages/*'], + }), + }) + + let workspaces + + workspaces = await getWorkspaces(['a', 'b'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + })), + 'should filter by package name' + ) + + workspaces = await getWorkspaces(['./packages/c'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + c: '{PATH}/packages/c', + })), + 'should filter by package directory' + ) + + workspaces = await getWorkspaces(['packages/c'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + c: '{PATH}/packages/c', + })), + 'should filter by rel package directory' + ) + + workspaces = await getWorkspaces([resolve(path, 'packages/c')], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + c: '{PATH}/packages/c', + })), + 'should filter by absolute package directory' + ) + + workspaces = await getWorkspaces(['packages'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + c: '{PATH}/packages/c', + d: '{PATH}/packages/d', + e: '{PATH}/packages/e', + noscripts: '{PATH}/packages/noscripts', + })), + 'should filter by parent directory name' + ) + + workspaces = await getWorkspaces(['./packages/'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + c: '{PATH}/packages/c', + d: '{PATH}/packages/d', + e: '{PATH}/packages/e', + noscripts: '{PATH}/packages/noscripts', + })), + 'should filter by parent directory path' + ) + + workspaces = await getWorkspaces([resolve(path, './packages')], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + c: '{PATH}/packages/c', + d: '{PATH}/packages/d', + e: '{PATH}/packages/e', + noscripts: '{PATH}/packages/noscripts', + })), + 'should filter by absolute parent directory path' + ) + + workspaces = await getWorkspaces([], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + c: '{PATH}/packages/c', + d: '{PATH}/packages/d', + e: '{PATH}/packages/e', + noscripts: '{PATH}/packages/noscripts', + })), + 'should return all workspaces if no filter set' + ) + + try { + await getWorkspaces(['missing'], { path }) + throw new Error('missed throw') + } catch (err) { + t.match( + err, + /No workspaces found/, + 'should throw no workspaces found error' + ) + } + + const unconfiguredWorkspaces = t.testdir({ + 'package.json': JSON.stringify({ + name: 'no-configured-workspaces', + version: '1.0.0', + }), + }) + try { + await getWorkspaces([], { path: unconfiguredWorkspaces }) + throw new Error('missed throw') + } catch (err) { + t.match( + err, + /No workspaces found/, + 'should throw no workspaces found error' + ) + } +}) |