diff options
Diffstat (limited to 'deps/npm/test/lib')
-rw-r--r-- | deps/npm/test/lib/ci.js | 19 | ||||
-rw-r--r-- | deps/npm/test/lib/completion.js | 587 | ||||
-rw-r--r-- | deps/npm/test/lib/doctor.js | 943 | ||||
-rw-r--r-- | deps/npm/test/lib/fund.js | 2 | ||||
-rw-r--r-- | deps/npm/test/lib/npm.js | 1 | ||||
-rw-r--r-- | deps/npm/test/lib/search.js | 193 | ||||
-rw-r--r-- | deps/npm/test/lib/team.js | 572 | ||||
-rw-r--r-- | deps/npm/test/lib/utils/flat-options.js | 22 | ||||
-rw-r--r-- | deps/npm/test/lib/utils/reify-output.js | 2 |
9 files changed, 2335 insertions, 6 deletions
diff --git a/deps/npm/test/lib/ci.js b/deps/npm/test/lib/ci.js index 8ddb8f8aad..c32fb83279 100644 --- a/deps/npm/test/lib/ci.js +++ b/deps/npm/test/lib/ci.js @@ -6,7 +6,16 @@ const { test } = require('tap') const requireInject = require('require-inject') -test('should use Arborist', (t) => { +test('should use Arborist and run-script', (t) => { + const scripts = [ + 'preinstall', + 'install', + 'postinstall', + 'prepublish', // XXX should we remove this finally?? + 'preprepare', + 'prepare', + 'postprepare', + ] const ci = requireInject('../../lib/ci.js', { '../../lib/npm.js': { prefix: 'foo', @@ -15,6 +24,9 @@ test('should use Arborist', (t) => { }, }, '../../lib/utils/reify-finish.js': async () => {}, + '@npmcli/run-script': opts => { + t.match(opts, { event: scripts.shift() }) + }, '@npmcli/arborist': function (args) { t.ok(args, 'gets options object') this.loadVirtual = () => { @@ -40,6 +52,7 @@ test('should use Arborist', (t) => { ci(null, er => { if (er) throw er + t.strictSame(scripts, [], 'called all scripts') t.end() }) }) @@ -53,6 +66,7 @@ test('should pass flatOptions to Arborist.reify', (t) => { }, }, '../../lib/utils/reify-finish.js': async () => {}, + '@npmcli/run-script': opts => {}, '@npmcli/arborist': function () { this.loadVirtual = () => Promise.resolve(true) this.reify = async (options) => { @@ -80,6 +94,7 @@ test('should throw if package-lock.json or npm-shrinkwrap missing', (t) => { global: false, }, }, + '@npmcli/run-script': opts => {}, '../../lib/utils/reify-finish.js': async () => {}, npmlog: { verbose: () => { @@ -102,6 +117,7 @@ test('should throw ECIGLOBAL', (t) => { global: true, }, }, + '@npmcli/run-script': opts => {}, '../../lib/utils/reify-finish.js': async () => {}, }) ci(null, (err, res) => { @@ -125,6 +141,7 @@ test('should remove existing node_modules before installing', (t) => { global: false, }, }, + '@npmcli/run-script': opts => {}, '../../lib/utils/reify-finish.js': async () => {}, '@npmcli/arborist': function () { this.loadVirtual = () => Promise.resolve(true) diff --git a/deps/npm/test/lib/completion.js b/deps/npm/test/lib/completion.js new file mode 100644 index 0000000000..367a1c03ab --- /dev/null +++ b/deps/npm/test/lib/completion.js @@ -0,0 +1,587 @@ +const { test } = require('tap') +const requireInject = require('require-inject') +const fs = require('fs') +const path = require('path') + +const completionScript = fs.readFileSync(path.resolve(__dirname, '../../lib/utils/completion.sh'), { encoding: 'utf8' }).replace(/^#!.*?\n/, '') + +const output = [] +const npmConfig = {} +let accessCompletionError = false + +const npm = { + config: { + set: (key, value) => { + npmConfig[key] = value + }, + clear: () => { + for (const key in npmConfig) + delete npmConfig[key] + }, + }, + commands: { + completion: { + completion: (opts, cb) => { + return cb(null, [['>>', '~/.bashrc']]) + }, + }, + adduser: {}, + access: { + completion: (opts, cb) => { + if (accessCompletionError) + return cb(new Error('access completion failed')) + + return cb(null, ['public', 'restricted']) + }, + }, + donothing: { + completion: (opts, cb) => { + return cb(null, null) + }, + }, + driveaboat: { + completion: (opts, cb) => { + // the leading space here is to exercise the escape method + return cb(null, ' fast') + }, + }, + }, +} + +const cmdList = { + aliases: { + login: 'adduser', + }, + cmdList: [ + 'access', + 'adduser', + 'completion', + ], + plumbing: [], +} + +const config = { + types: { + global: Boolean, + browser: [null, Boolean, String], + registry: [null, String], + }, + shorthands: { + reg: ['--registry'], + }, +} + +const deref = (cmd) => { + return cmd +} + +const completion = requireInject('../../lib/completion.js', { + '../../lib/npm.js': npm, + '../../lib/utils/cmd-list.js': cmdList, + '../../lib/utils/config.js': config, + '../../lib/utils/deref-command.js': deref, + '../../lib/utils/is-windows-shell.js': false, + '../../lib/utils/output.js': (line) => { + output.push(line) + }, +}) + +test('completion completion', t => { + const home = process.env.HOME + t.teardown(() => { + process.env.HOME = home + }) + + process.env.HOME = t.testdir({ + '.bashrc': '', + '.zshrc': '', + }) + + completion.completion({ w: 2 }, (err, res) => { + if (err) + throw err + + t.strictSame(res, [ + ['>>', '~/.zshrc'], + ['>>', '~/.bashrc'], + ], 'identifies both shells') + t.end() + }) +}) + +test('completion completion no known shells', t => { + const home = process.env.HOME + t.teardown(() => { + process.env.HOME = home + }) + + process.env.HOME = t.testdir() + + completion.completion({ w: 2 }, (err, res) => { + if (err) + throw err + + t.strictSame(res, [], 'no responses') + t.end() + }) +}) + +test('completion completion wrong word count', t => { + completion.completion({ w: 3 }, (err, res) => { + if (err) + throw err + + t.strictSame(res, undefined, 'no responses') + t.end() + }) +}) + +test('completion errors in windows without bash', t => { + const compl = requireInject('../../lib/completion.js', { + '../../lib/utils/is-windows-shell.js': true, + }) + + compl({}, (err) => { + t.match(err, { + code: 'ENOTSUP', + message: /completion supported only in MINGW/, + }, 'returns the correct error') + t.end() + }) +}) + +test('dump script when completion is not being attempted', t => { + const _write = process.stdout.write + const _on = process.stdout.on + t.teardown(() => { + process.stdout.write = _write + process.stdout.on = _on + }) + + let errorHandler + process.stdout.on = (event, handler) => { + errorHandler = handler + process.stdout.on = _on + } + + let data + process.stdout.write = (chunk, callback) => { + data = chunk + process.stdout.write = _write + process.nextTick(() => { + callback() + errorHandler({ errno: 'EPIPE' }) + }) + } + + completion({}, (err) => { + if (err) + throw err + + t.equal(data, completionScript, 'wrote the completion script') + t.end() + }) +}) + +test('dump script exits correctly when EPIPE is emitted on stdout', t => { + const _write = process.stdout.write + const _on = process.stdout.on + t.teardown(() => { + process.stdout.write = _write + process.stdout.on = _on + }) + + let errorHandler + process.stdout.on = (event, handler) => { + errorHandler = handler + process.stdout.on = _on + } + + let data + process.stdout.write = (chunk, callback) => { + data = chunk + process.stdout.write = _write + process.nextTick(() => { + errorHandler({ errno: 'EPIPE' }) + callback() + }) + } + + completion({}, (err) => { + if (err) + throw err + + t.equal(data, completionScript, 'wrote the completion script') + t.end() + }) +}) + +test('non EPIPE errors cause failures', t => { + const _write = process.stdout.write + const _on = process.stdout.on + t.teardown(() => { + process.stdout.write = _write + process.stdout.on = _on + }) + + let errorHandler + process.stdout.on = (event, handler) => { + errorHandler = handler + process.stdout.on = _on + } + + let data + process.stdout.write = (chunk, callback) => { + data = chunk + process.stdout.write = _write + process.nextTick(() => { + errorHandler({ errno: 'ESOMETHINGELSE' }) + callback() + }) + } + + completion({}, (err) => { + t.equal(err.errno, 'ESOMETHINGELSE', 'propagated error') + t.equal(data, completionScript, 'wrote the completion script') + t.end() + }) +}) + +test('completion completes single command name', t => { + process.env.COMP_CWORD = 1 + process.env.COMP_LINE = 'npm c' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', 'c'], (err, res) => { + if (err) + throw err + + t.strictSame(output, ['completion'], 'correctly completed a command name') + t.end() + }) +}) + +test('completion completes command names', t => { + process.env.COMP_CWORD = 1 + process.env.COMP_LINE = 'npm a' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', 'a'], (err, res) => { + if (err) + throw err + + t.strictSame(output, [['access', 'adduser'].join('\n')], 'correctly completed a command name') + t.end() + }) +}) + +test('completion of invalid command name does nothing', t => { + process.env.COMP_CWORD = 1 + process.env.COMP_LINE = 'npm compute' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', 'compute'], (err, res) => { + if (err) + throw err + + t.strictSame(output, [], 'returns no results') + t.end() + }) +}) + +test('completion triggers command completions', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm access ' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', 'access', ''], (err, res) => { + if (err) + throw err + + t.strictSame(npmConfig, { + argv: { + remain: ['npm', 'access'], + cooked: ['npm', 'access'], + original: ['npm', 'access'], + }, + }, 'applies command config appropriately') + t.strictSame(output, [['public', 'restricted'].join('\n')], 'correctly completed a subcommand name') + t.end() + }) +}) + +test('completion triggers filtered command completions', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm access p' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', 'access', 'p'], (err, res) => { + if (err) + throw err + + t.strictSame(npmConfig, { + argv: { + remain: ['npm', 'access'], + cooked: ['npm', 'access'], + original: ['npm', 'access'], + }, + }, 'applies command config appropriately') + t.strictSame(output, ['public'], 'correctly completed a subcommand name') + t.end() + }) +}) + +test('completions for commands that return nested arrays are joined', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm completion ' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', 'completion', ''], (err, res) => { + if (err) + throw err + + t.strictSame(npmConfig, { + argv: { + remain: ['npm', 'completion'], + cooked: ['npm', 'completion'], + original: ['npm', 'completion'], + }, + }, 'applies command config appropriately') + t.strictSame(output, ['>> ~/.bashrc'], 'joins nested arrays') + t.end() + }) +}) + +test('completions for commands that return nothing work correctly', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm donothing ' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', 'donothing', ''], (err, res) => { + if (err) + throw err + + t.strictSame(npmConfig, { + argv: { + remain: ['npm', 'donothing'], + cooked: ['npm', 'donothing'], + original: ['npm', 'donothing'], + }, + }, 'applies command config appropriately') + t.strictSame(output, [], 'returns nothing') + t.end() + }) +}) + +test('completions for commands that return a single item work correctly', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm driveaboat ' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', 'driveaboat', ''], (err, res) => { + if (err) + throw err + + t.strictSame(npmConfig, { + argv: { + remain: ['npm', 'driveaboat'], + cooked: ['npm', 'driveaboat'], + original: ['npm', 'driveaboat'], + }, + }, 'applies command config appropriately') + t.strictSame(output, ['\' fast\''], 'returns the correctly escaped string') + t.end() + }) +}) + +test('command completion for commands with no completion return no results', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm adduser ' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + // quotes around adduser are to ensure coverage when unescaping commands + completion(['npm', '\'adduser\'', ''], (err, res) => { + if (err) + throw err + + t.strictSame(npmConfig, { + argv: { + remain: ['npm', 'adduser'], + cooked: ['npm', 'adduser'], + original: ['npm', 'adduser'], + }, + }, 'applies command config appropriately') + t.strictSame(output, [], 'correctly completed a subcommand name') + t.end() + }) +}) + +test('command completion errors propagate', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm access ' + process.env.COMP_POINT = process.env.COMP_LINE.length + accessCompletionError = true + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + accessCompletionError = false + }) + + completion(['npm', 'access', ''], (err, res) => { + t.match(err, /access completion failed/, 'catches the appropriate error') + t.strictSame(npmConfig, { + argv: { + remain: ['npm', 'access'], + cooked: ['npm', 'access'], + original: ['npm', 'access'], + }, + }, 'applies command config appropriately') + t.strictSame(output, [], 'returns no results') + t.end() + }) +}) + +test('completion can complete flags', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm install --' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', 'install', '--'], (err, res) => { + if (err) + throw err + + t.strictSame(npmConfig, {}, 'does not apply command config') + t.strictSame(output, [['--global', '--browser', '--registry', '--reg', '--no-global', '--no-browser'].join('\n')], 'correctly completes flag names') + t.end() + }) +}) + +test('double dashes escape from flag completion', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm -- install --' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', '--', 'install', '--'], (err, res) => { + if (err) + throw err + + t.strictSame(npmConfig, {}, 'does not apply command config') + t.strictSame(output, [['access', 'adduser', 'completion', 'login'].join('\n')], 'correctly completes flag names') + t.end() + }) +}) + +test('completion cannot complete options that take a value in mid-command', t => { + process.env.COMP_CWORD = 2 + process.env.COMP_LINE = 'npm --registry install' + process.env.COMP_POINT = process.env.COMP_LINE.length + + t.teardown(() => { + delete process.env.COMP_CWORD + delete process.env.COMP_LINE + delete process.env.COMP_POINT + npm.config.clear() + output.length = 0 + }) + + completion(['npm', '--registry', 'install'], (err, res) => { + if (err) + throw err + + t.strictSame(npmConfig, {}, 'does not apply command config') + t.strictSame(output, [], 'does not try to complete option arguments in the middle of a command') + t.end() + }) +}) diff --git a/deps/npm/test/lib/doctor.js b/deps/npm/test/lib/doctor.js new file mode 100644 index 0000000000..c2c3fdb978 --- /dev/null +++ b/deps/npm/test/lib/doctor.js @@ -0,0 +1,943 @@ +const { test } = require('tap') +const requireInject = require('require-inject') + +const { join } = require('path') +const fs = require('fs') +const ansiTrim = require('../../lib/utils/ansi-trim.js') +const isWindows = require('../../lib/utils/is-windows.js') + +// getuid and getgid do not exist in windows, so we shim them +// to return 0, as that is the value that lstat will assign the +// gid and uid properties for fs.Stats objects +if (isWindows) { + process.getuid = () => 0 + process.getgid = () => 0 +} + +const output = [] + +let pingError +const ping = async () => { + if (pingError) + throw pingError +} + +const nodeVersions = [ + { version: 'v14.0.0', lts: false }, + { version: 'v13.0.0', lts: false }, + // it's necessary to allow tests in node 10.x to not mark 12.x as lts + { version: 'v12.0.0', lts: false }, + { version: 'v10.13.0', lts: 'Dubnium' }, +] + +const fetch = async () => { + return { + json: async () => { + return nodeVersions + }, + } +} + +const logs = { + info: [], +} + +const clearLogs = (obj = logs) => { + output.length = 0 + for (const key in obj) { + if (Array.isArray(obj[key])) + obj[key].length = 0 + else + delete obj[key] + } +} + +const npm = { + flatOptions: { + registry: 'https://registry.npmjs.org/', + }, + log: { + info: (msg) => { + logs.info.push(msg) + }, + newItem: (name) => { + logs[name] = {} + + return { + info: (_, msg) => { + if (!logs[name].info) + logs[name].info = [] + logs[name].info.push(msg) + }, + warn: (_, msg) => { + if (!logs[name].warn) + logs[name].warn = [] + logs[name].warn.push(msg) + }, + error: (_, msg) => { + if (!logs[name].error) + logs[name].error = [] + logs[name].error.push(msg) + }, + silly: (_, msg) => { + if (!logs[name].silly) + logs[name].silly = [] + logs[name].silly.push(msg) + }, + completeWork: () => {}, + finish: () => { + logs[name].finished = true + }, + } + }, + level: 'error', + levels: { + info: 1, + error: 0, + }, + }, + version: '7.1.0', +} + +let latestNpm = npm.version +const pacote = { + manifest: async () => { + return { version: latestNpm } + }, +} + +let whichError = null +const which = async () => { + if (whichError) + throw whichError + return '/path/to/git' +} + +let verifyResponse = { verifiedCount: 1, verifiedContent: 1 } +const cacache = { + verify: async () => { + return verifyResponse + }, +} + +const doctor = requireInject('../../lib/doctor.js', { + '../../lib/utils/is-windows.js': false, + '../../lib/utils/ping.js': ping, + '../../lib/utils/output.js': (data) => { + output.push(data) + }, + '../../lib/npm.js': npm, + cacache, + pacote, + 'make-fetch-happen': fetch, + which, +}) + +test('npm doctor checks ok', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + clearLogs() + }) + + doctor([], (err) => { + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor supports silent', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + npm.log.level = 'info' + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + npm.log.level = 'error' + clearLogs() + }) + + doctor([], (err) => { + if (err) { + t.fail(err) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.strictSame(output, [], 'did not print output') + t.end() + }) +}) + +test('npm doctor supports color', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + npm.color = true + pingError = { message: 'generic error' } + const _consoleError = console.error + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + delete npm.color + pingError = null + console.error = _consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the ping error') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping.*not ok/, 'ping output is ok') + t.match(output, /npm -v.*ok/, 'npm -v output is ok') + t.match(output, /node -v.*ok/, 'node -v output is ok') + t.match(output, /npm config get registry.*ok.*using default/, 'npm config get registry output is ok') + t.match(output, /which git.*ok/, 'which git output is ok') + t.match(output, /cached files.*ok/, 'cached files are ok') + t.match(output, /local node_modules.*ok/, 'local node_modules are ok') + t.match(output, /global node_modules.*ok/, 'global node_modules are ok') + t.match(output, /local bin folder.*ok/, 'local bin is ok') + t.match(output, /global bin folder.*ok/, 'global bin is ok') + t.match(output, /cache contents.*ok/, 'cache contents is ok') + t.notEqual(output[0], ansiTrim(output[0]), 'output should contain color codes') + t.end() + }) +}) + +test('npm doctor skips some tests in windows', t => { + const winDoctor = requireInject('../../lib/doctor.js', { + '../../lib/utils/is-windows.js': true, + '../../lib/utils/ping.js': ping, + '../../lib/utils/output.js': (data) => { + output.push(data) + }, + '../../lib/npm.js': npm, + cacache, + pacote, + 'make-fetch-happen': fetch, + which, + }) + + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + clearLogs() + }) + + winDoctor([], (err) => { + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: undefined, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor ping error E{3}', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + pingError = { code: 'E111', message: 'this error is 111' } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + pingError = null + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the ping error') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*not ok\s*111 this error is 111/, 'ping output contains trimmed error') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor generic ping error', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + pingError = { message: 'generic error' } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + pingError = null + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the ping error') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*not ok\s*generic error/, 'ping output contains trimmed error') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor outdated npm version', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + latestNpm = '7.1.1' + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + latestNpm = npm.version + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the out of date npm') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*not ok/, 'npm -v output is not ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor outdated nodejs version', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + nodeVersions.push({ version: process.version.replace(/\d+$/, '999'), lts: false }) + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + nodeVersions.pop() + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the out of date nodejs') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*not ok/, 'node -v output is not ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor file permission checks', t => { + const dir = t.testdir({ + cache: { + one: 'one', + link: t.fixture('symlink', './one'), + unreadable: 'unreadable', + baddir: {}, + }, + local: { + two: 'two', + notmine: 'notmine', + }, + global: { + three: 'three', + broken: 'broken', + }, + localBin: { + four: 'four', + five: 'five', + }, + globalBin: { + six: 'six', + seven: 'seven', + }, + }) + + const _fsLstat = fs.lstat + fs.lstat = (p, cb) => { + let err = null + let stat = null + + try { + stat = fs.lstatSync(p) + } catch (err) { + return cb(err) + } + + switch (p) { + case join(dir, 'local', 'notmine'): + stat.uid += 1 + stat.gid += 1 + break + case join(dir, 'global', 'broken'): + err = new Error('broken') + break + } + + return cb(err, stat) + } + + const _fsReaddir = fs.readdir + fs.readdir = (p, cb) => { + let err = null + let result = null + + try { + result = fs.readdirSync(p) + } catch (err) { + return cb(err) + } + + if (p === join(dir, 'cache', 'baddir')) + err = new Error('broken') + + return cb(err, result) + } + + const _fsAccess = fs.access + fs.access = (p, mask, cb) => { + const err = new Error('failed') + switch (p) { + case join(dir, 'cache', 'unreadable'): + case join(dir, 'localBin', 'four'): + case join(dir, 'globalBin', 'six'): + return cb(err) + default: + return cb(null) + } + } + + const doctor = requireInject('../../lib/doctor.js', { + '../../lib/utils/is-windows.js': false, + '../../lib/utils/ping.js': ping, + '../../lib/utils/output.js': (data) => { + output.push(data) + }, + '../../lib/npm.js': npm, + cacache, + pacote, + 'make-fetch-happen': fetch, + which, + fs, + }) + // it's necessary to allow tests in node 10.x to not mark 12.x as lted + + npm.cache = npm.flatOptions.cache = join(dir, 'cache') + npm.localDir = join(dir, 'local') + npm.globalDir = join(dir, 'global') + npm.localBin = join(dir, 'localBin') + npm.globalBin = join(dir, 'globalBin') + const _consoleError = console.error + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + console.error = _consoleError + fs.lstat = _fsLstat + fs.readdir = _fsReaddir + fs.access = _fsAccess + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'identified problems') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [join(dir, 'cache')]: { finished: true }, + [join(dir, 'local')]: { finished: true }, + [join(dir, 'global')]: { finished: true }, + [join(dir, 'localBin')]: { finished: true }, + [join(dir, 'globalBin')]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*not ok/, 'cached files are not ok') + t.match(output, /local node_modules\s*not ok/, 'local node_modules are not ok') + t.match(output, /global node_modules\s*not ok/, 'global node_modules are not ok') + t.match(output, /local bin folder\s*not ok/, 'local bin is not ok') + t.match(output, /global bin folder\s*not ok/, 'global bin is not ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor missing git', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + whichError = new Error('boom') + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + whichError = null + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + t.match(err, /Some problems found/, 'detected the missing git') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*not ok/, 'which git output is not ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) + +test('npm doctor cache verification showed bad content', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + const _verifyResponse = verifyResponse + verifyResponse = { + ...verifyResponse, + badContentCount: 1, + } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + verifyResponse = _verifyResponse + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + // cache verification problems get fixed and so do not throw an error + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is not ok') + t.end() + }) +}) + +test('npm doctor cache verification showed reclaimed content', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + const _verifyResponse = verifyResponse + verifyResponse = { + ...verifyResponse, + reclaimedCount: 1, + reclaimedSize: 100, + } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + verifyResponse = _verifyResponse + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + // cache verification problems get fixed and so do not throw an error + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is not ok') + t.end() + }) +}) + +test('npm doctor cache verification showed missing content', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + const _verifyResponse = verifyResponse + verifyResponse = { + ...verifyResponse, + missingContent: 1, + } + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + verifyResponse = _verifyResponse + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + // cache verification problems get fixed and so do not throw an error + if (err) { + t.fail(output) + return t.end() + } + + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*ok\s*using default/, 'npm config get registry output is ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is not ok') + t.end() + }) +}) + +test('npm doctor not using default registry', t => { + const dir = t.testdir() + npm.cache = npm.flatOptions.cache = dir + npm.localDir = dir + npm.globalDir = dir + npm.localBin = dir + npm.globalBin = dir + const _currentRegistry = npm.flatOptions.registry + npm.flatOptions.registry = 'https://google.com' + const consoleError = console.error + // we just print an empty line here, so swallow it and ignore + console.error = () => {} + + t.teardown(() => { + delete npm.cache + delete npm.flatOptions.cache + delete npm.localDir + delete npm.globalDir + delete npm.localBin + delete npm.globalBin + npm.flatOptions.registry = _currentRegistry + console.error = consoleError + clearLogs() + }) + + doctor([], (err) => { + // cache verification problems get fixed and so do not throw an error + t.match(err, /Some problems found/, 'detected the non-default registry') + t.match(logs, { + checkPing: { finished: true }, + getLatestNpmVersion: { finished: true }, + getLatestNodejsVersion: { finished: true }, + getGitPath: { finished: true }, + [dir]: { finished: true }, + verifyCachedFiles: { finished: true }, + }, 'trackers all finished') + t.match(output, /npm ping\s*ok/, 'ping output is ok') + t.match(output, /npm -v\s*ok/, 'npm -v output is ok') + t.match(output, /node -v\s*ok/, 'node -v output is ok') + t.match(output, /npm config get registry\s*not ok/, 'npm config get registry output is not ok') + t.match(output, /which git\s*ok/, 'which git output is ok') + t.match(output, /cached files\s*ok/, 'cached files are ok') + t.match(output, /local node_modules\s*ok/, 'local node_modules are ok') + t.match(output, /global node_modules\s*ok/, 'global node_modules are ok') + t.match(output, /local bin folder\s*ok/, 'local bin is ok') + t.match(output, /global bin folder\s*ok/, 'global bin is ok') + t.match(output, /cache contents\s*ok/, 'cache contents is ok') + t.end() + }) +}) diff --git a/deps/npm/test/lib/fund.js b/deps/npm/test/lib/fund.js index a23fc88ced..73f639b6ce 100644 --- a/deps/npm/test/lib/fund.js +++ b/deps/npm/test/lib/fund.js @@ -1,5 +1,3 @@ -'use strict' - const { test } = require('tap') const requireInject = require('require-inject') diff --git a/deps/npm/test/lib/npm.js b/deps/npm/test/lib/npm.js index 6bfb5b4376..2c71d229a7 100644 --- a/deps/npm/test/lib/npm.js +++ b/deps/npm/test/lib/npm.js @@ -1,4 +1,3 @@ -'use strict' const t = require('tap') const fs = require('fs') diff --git a/deps/npm/test/lib/search.js b/deps/npm/test/lib/search.js new file mode 100644 index 0000000000..1dba1250e0 --- /dev/null +++ b/deps/npm/test/lib/search.js @@ -0,0 +1,193 @@ +const Minipass = require('minipass') +const t = require('tap') +const requireInject = require('require-inject') +const libnpmsearchResultFixture = + require('../fixtures/libnpmsearch-stream-result.js') + +let result = '' +const flatOptions = { + search: { + exclude: null, + limit: 20, + opts: '', + }, +} +const npm = { flatOptions: { ...flatOptions } } +const npmlog = { + silly () {}, + clearProgress () {}, +} +const libnpmsearch = { + stream () {}, +} +const mocks = { + npmlog, + libnpmsearch, + '../../lib/npm.js': npm, + '../../lib/utils/output.js': (...msg) => { + result += msg.join('\n') + }, + '../../lib/utils/usage.js': () => 'usage instructions', + // '../../lib/search/format-package-stream.js': a => a, +} + +t.afterEach(cb => { + result = '' + npm.flatOptions = flatOptions + cb() +}) + +const search = requireInject('../../lib/search.js', mocks) + +t.test('no args', t => { + search([], err => { + t.match( + err, + /search must be called with arguments/, + 'should throw usage instructions' + ) + t.end() + }) +}) + +t.test('search <name>', t => { + const src = new Minipass() + src.objectMode = true + const libnpmsearch = { + stream () { + return src + }, + } + + const search = requireInject('../../lib/search.js', { + ...mocks, + libnpmsearch, + }) + + search(['libnpm'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should have expected search results') + + t.end() + }) + + for (const i of libnpmsearchResultFixture) + src.write(i) + + src.end() +}) + +t.test('search <name> --searchexclude --searchopts', t => { + npm.flatOptions.search = { + ...flatOptions.search, + exclude: '', + } + + const src = new Minipass() + src.objectMode = true + const libnpmsearch = { + stream () { + return src + }, + } + + const search = requireInject('../../lib/search.js', { + ...mocks, + libnpmsearch, + }) + + search(['foo'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should have filtered expected search results') + + t.end() + }) + + src.write({ + name: 'foo', + scope: 'unscoped', + version: '1.0.0', + description: '', + keywords: [], + date: null, + author: { name: 'Foo', email: 'foo@npmjs.com' }, + publisher: { name: 'Foo', email: 'foo@npmjs.com' }, + maintainers: [ + { username: 'foo', email: 'foo@npmjs.com' }, + ], + }) + src.write({ + name: 'libnpmversion', + scope: 'unscoped', + version: '1.0.0', + description: '', + keywords: [], + date: null, + author: { name: 'Foo', email: 'foo@npmjs.com' }, + publisher: { name: 'Foo', email: 'foo@npmjs.com' }, + maintainers: [ + { username: 'foo', email: 'foo@npmjs.com' }, + ], + }) + + src.end() +}) + +t.test('empty search results', t => { + const src = new Minipass() + src.objectMode = true + const libnpmsearch = { + stream () { + return src + }, + } + + const search = requireInject('../../lib/search.js', { + ...mocks, + libnpmsearch, + }) + + search(['foo'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should have expected search results') + + t.end() + }) + + src.end() +}) + +t.test('search api response error', t => { + const src = new Minipass() + src.objectMode = true + const libnpmsearch = { + stream () { + return src + }, + } + + const search = requireInject('../../lib/search.js', { + ...mocks, + libnpmsearch, + }) + + search(['foo'], err => { + t.match( + err, + /ERR/, + 'should throw response error' + ) + + t.end() + }) + + src.emit('error', new Error('ERR')) + + src.end() +}) diff --git a/deps/npm/test/lib/team.js b/deps/npm/test/lib/team.js new file mode 100644 index 0000000000..c534cc8327 --- /dev/null +++ b/deps/npm/test/lib/team.js @@ -0,0 +1,572 @@ +const t = require('tap') +const requireInject = require('require-inject') + +let result = '' +const libnpmteam = { + async add () {}, + async create () {}, + async destroy () {}, + async lsTeams () {}, + async lsUsers () {}, + async rm () {}, +} +const npm = { flatOptions: {} } +const mocks = { + libnpmteam, + 'cli-columns': a => a.join(' '), + '../../lib/npm.js': npm, + '../../lib/utils/output.js': (...msg) => { + result += msg.join('\n') + }, + '../../lib/utils/otplease.js': async (opts, fn) => fn(opts), + '../../lib/utils/usage.js': () => 'usage instructions', +} + +t.afterEach(cb => { + result = '' + npm.flatOptions = {} + cb() +}) + +const team = requireInject('../../lib/team.js', mocks) + +t.test('no args', t => { + team([], err => { + t.match( + err, + 'usage instructions', + 'should throw usage instructions' + ) + t.end() + }) +}) + +t.test('team add <scope:team> <user>', t => { + t.test('default output', t => { + team(['add', '@npmcli:developers', 'foo'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should output success result for add user') + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + team(['add', '@npmcli:developers', 'foo'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output success result for parseable add user' + ) + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + team(['add', '@npmcli:developers', 'foo'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + { + added: true, + team: 'npmcli:developers', + user: 'foo', + }, + 'should output success result for add user json' + ) + t.end() + }) + }) + + t.test('--silent', t => { + npm.flatOptions.silent = true + + team(['add', '@npmcli:developers', 'foo'], err => { + if (err) + throw err + + t.deepEqual(result, '', 'should not output success if silent') + t.end() + }) + }) + + t.end() +}) + +t.test('team create <scope:team>', t => { + t.test('default output', t => { + team(['create', '@npmcli:newteam'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should output success result for create team') + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + team(['create', '@npmcli:newteam'], err => { + if (err) + throw err + + t.matchSnapshot( + result, + 'should output parseable success result for create team' + ) + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + team(['create', '@npmcli:newteam'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + { + created: true, + team: 'npmcli:newteam', + }, + 'should output success result for create team' + ) + t.end() + }) + }) + + t.test('--silent', t => { + npm.flatOptions.silent = true + + team(['create', '@npmcli:newteam'], err => { + if (err) + throw err + + t.deepEqual(result, '', 'should not output create success if silent') + t.end() + }) + }) + + t.end() +}) + +t.test('team destroy <scope:team>', t => { + t.test('default output', t => { + team(['destroy', '@npmcli:newteam'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should output success result for destroy team') + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + team(['destroy', '@npmcli:newteam'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should output parseable result for destroy team') + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + team(['destroy', '@npmcli:newteam'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + { + deleted: true, + team: 'npmcli:newteam', + }, + 'should output parseable result for destroy team' + ) + t.end() + }) + }) + + t.test('--silent', t => { + npm.flatOptions.silent = true + + team(['destroy', '@npmcli:newteam'], err => { + if (err) + throw err + + t.deepEqual(result, '', 'should not output destroy if silent') + t.end() + }) + }) + + t.end() +}) + +t.test('team ls <scope>', t => { + const libnpmteam = { + async lsTeams () { + return [ + 'npmcli:developers', + 'npmcli:designers', + 'npmcli:product', + ] + }, + } + + const team = requireInject('../../lib/team.js', { + ...mocks, + libnpmteam, + }) + + t.test('default output', t => { + team(['ls', '@npmcli'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should list teams for a given scope') + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + team(['ls', '@npmcli'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should list teams for a parseable scope') + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + team(['ls', '@npmcli'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + [ + 'npmcli:designers', + 'npmcli:developers', + 'npmcli:product', + ], + 'should json list teams for a scope json' + ) + t.end() + }) + }) + + t.test('--silent', t => { + npm.flatOptions.silent = true + + team(['ls', '@npmcli'], err => { + if (err) + throw err + + t.deepEqual(result, '', 'should not list teams if silent') + t.end() + }) + }) + + t.test('no teams', t => { + const libnpmteam = { + async lsTeams () { + return [] + }, + } + + const team = requireInject('../../lib/team.js', { + ...mocks, + libnpmteam, + }) + + team(['ls', '@npmcli'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should list no teams for a given scope') + t.end() + }) + }) + + t.test('single team', t => { + const libnpmteam = { + async lsTeams () { + return ['npmcli:developers'] + }, + } + + const team = requireInject('../../lib/team.js', { + ...mocks, + libnpmteam, + }) + + team(['ls', '@npmcli'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should list single team for a given scope') + t.end() + }) + }) + + t.end() +}) + +t.test('team ls <scope:team>', t => { + const libnpmteam = { + async lsUsers () { + return ['nlf', 'ruyadorno', 'darcyclarke', 'isaacs'] + }, + } + const team = requireInject('../../lib/team.js', { + ...mocks, + libnpmteam, + }) + + t.test('default output', t => { + team(['ls', '@npmcli:developers'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should list users for a given scope:team') + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + team(['ls', '@npmcli:developers'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should list users for a parseable scope:team') + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + team(['ls', '@npmcli:developers'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + [ + 'darcyclarke', + 'isaacs', + 'nlf', + 'ruyadorno', + ], + 'should list users for a scope:team json' + ) + t.end() + }) + }) + + t.test('--silent', t => { + npm.flatOptions.silent = true + + team(['ls', '@npmcli:developers'], err => { + if (err) + throw err + + t.deepEqual(result, '', 'should not output users if silent') + t.end() + }) + }) + + t.test('no users', t => { + const libnpmteam = { + async lsUsers () { + return [] + }, + } + + const team = requireInject('../../lib/team.js', { + ...mocks, + libnpmteam, + }) + + team(['ls', '@npmcli:developers'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should list no users for a given scope') + t.end() + }) + }) + + t.test('single user', t => { + const libnpmteam = { + async lsUsers () { + return ['foo'] + }, + } + + const team = requireInject('../../lib/team.js', { + ...mocks, + libnpmteam, + }) + + team(['ls', '@npmcli:developers'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should list single user for a given scope') + t.end() + }) + }) + + t.end() +}) + +t.test('team rm <scope:team> <user>', t => { + t.test('default output', t => { + team(['rm', '@npmcli:newteam', 'foo'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should output success result for remove user') + t.end() + }) + }) + + t.test('--parseable', t => { + npm.flatOptions.parseable = true + + team(['rm', '@npmcli:newteam', 'foo'], err => { + if (err) + throw err + + t.matchSnapshot(result, 'should output parseable result for remove user') + t.end() + }) + }) + + t.test('--json', t => { + npm.flatOptions.json = true + + team(['rm', '@npmcli:newteam', 'foo'], err => { + if (err) + throw err + + t.deepEqual( + JSON.parse(result), + { + removed: true, + team: 'npmcli:newteam', + user: 'foo', + }, + 'should output json result for remove user' + ) + t.end() + }) + }) + + t.test('--silent', t => { + npm.flatOptions.silent = true + + team(['rm', '@npmcli:newteam', 'foo'], err => { + if (err) + throw err + + t.deepEqual(result, '', 'should not output rm result if silent') + t.end() + }) + }) + + t.end() +}) + +t.test('completion', t => { + const { completion } = team + + t.test('npm team autocomplete', t => { + completion({ + conf: { + argv: { + remain: ['npm', 'team'], + }, + }, + }, (err, res) => { + if (err) + throw err + + t.strictSame( + res, + ['create', 'destroy', 'add', 'rm', 'ls'], + 'should auto complete with subcommands' + ) + + t.end() + }) + }) + + t.test('npm team <subcommand> autocomplete', async t => { + const check = (subcmd) => new Promise((res, rej) => + completion({ + conf: { + argv: { + remain: ['npm', 'team', subcmd], + }, + }, + }, (err, response) => { + if (err) + rej(err) + + t.strictSame( + response, + [], + `should not autocomplete ${subcmd} subcommand` + ) + res() + })) + + await ['create', 'destroy', 'add', 'rm', 'ls'].map(check) + }) + + t.test('npm team unknown subcommand autocomplete', t => { + completion({ + conf: { + argv: { + remain: ['npm', 'team', 'missing-subcommand'], + }, + }, + }, (err, res) => { + t.match( + err, + /missing-subcommand not recognized/, + 'should throw a a not recognized error' + ) + + t.end() + }) + }) + + t.end() +}) diff --git a/deps/npm/test/lib/utils/flat-options.js b/deps/npm/test/lib/utils/flat-options.js index 3cbf06a48b..ee7620fa78 100644 --- a/deps/npm/test/lib/utils/flat-options.js +++ b/deps/npm/test/lib/utils/flat-options.js @@ -1,6 +1,7 @@ const t = require('tap') process.env.NODE = '/path/to/some/node' +process.env.NODE_ENV = 'development' const logs = [] const log = require('npmlog') @@ -195,43 +196,56 @@ t.test('tag emits warning', t => { 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 => { @@ -241,26 +255,34 @@ t.test('omit/include options', t => { 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() }) diff --git a/deps/npm/test/lib/utils/reify-output.js b/deps/npm/test/lib/utils/reify-output.js index b905c9ab0f..f7fd96ee87 100644 --- a/deps/npm/test/lib/utils/reify-output.js +++ b/deps/npm/test/lib/utils/reify-output.js @@ -1,5 +1,3 @@ -'use strict' - const t = require('tap') const requireInject = require('require-inject') |