const t = require('tap') const requireInject = require('require-inject') const npm = { prefix: null, color: true, flatOptions: {}, } const { resolve } = require('path') const OUTPUT = [] const explain = requireInject('../../lib/explain.js', { '../../lib/npm.js': npm, '../../lib/utils/output.js': (...args) => { OUTPUT.push(args) }, // keep the snapshots pared down a bit, since this has its own tests. '../../lib/utils/explain-dep.js': { explainNode: (expl, depth, color) => { return `${expl.name}@${expl.version} depth=${depth} color=${color}` }, }, }) t.test('no args throws usage', async t => { t.plan(1) try { await explain([], er => { throw er }) } catch (er) { t.equal(er, explain.usage) } }) t.test('no match throws not found', async t => { npm.prefix = t.testdir() t.plan(1) try { await explain(['foo@1.2.3', 'node_modules/baz'], er => { throw er }) } catch (er) { t.equal(er, 'No dependencies found matching foo@1.2.3, node_modules/baz') } }) t.test('invalid package name throws not found', async t => { npm.prefix = t.testdir() t.plan(1) const badName = ' not a valid package name ' try { await explain([`${badName}@1.2.3`], er => { throw er }) } catch (er) { t.equal(er, `No dependencies found matching ${badName}@1.2.3`) } }) t.test('explain some nodes', async t => { npm.prefix = t.testdir({ node_modules: { foo: { 'package.json': JSON.stringify({ name: 'foo', version: '1.2.3', dependencies: { bar: '*', }, }), }, bar: { 'package.json': JSON.stringify({ name: 'bar', version: '1.2.3', }), }, baz: { 'package.json': JSON.stringify({ name: 'baz', version: '1.2.3', dependencies: { foo: '*', bar: '2', }, }), node_modules: { bar: { 'package.json': JSON.stringify({ name: 'bar', version: '2.3.4', }), }, extra: { 'package.json': JSON.stringify({ name: 'extra', version: '99.9999.999999', description: 'extraneous package', }), }, }, }, }, 'package.json': JSON.stringify({ dependencies: { baz: '1', }, }), }) // works with either a full actual path or the location const p = 'node_modules/foo' for (const path of [p, resolve(npm.prefix, p)]) { await explain([path], er => { if (er) throw er }) t.strictSame(OUTPUT, [['foo@1.2.3 depth=Infinity color=true']]) OUTPUT.length = 0 } // finds all nodes by name await explain(['bar'], er => { if (er) throw er }) t.strictSame(OUTPUT, [[ 'bar@1.2.3 depth=Infinity color=true\n\n' + 'bar@2.3.4 depth=Infinity color=true', ]]) OUTPUT.length = 0 // finds only nodes that match the spec await explain(['bar@1'], er => { if (er) throw er }) t.strictSame(OUTPUT, [['bar@1.2.3 depth=Infinity color=true']]) OUTPUT.length = 0 // finds extraneous nodes await explain(['extra'], er => { if (er) throw er }) t.strictSame(OUTPUT, [['extra@99.9999.999999 depth=Infinity color=true']]) OUTPUT.length = 0 npm.flatOptions.json = true await explain(['node_modules/foo'], er => { if (er) throw er }) t.match(JSON.parse(OUTPUT[0][0]), [{ name: 'foo', version: '1.2.3', dependents: Array, }]) OUTPUT.length = 0 npm.flatOptions.json = false t.test('report if no nodes found', async t => { t.plan(1) await explain(['asdf/foo/bar', 'quux@1.x'], er => { t.equal(er, 'No dependencies found matching asdf/foo/bar, quux@1.x') }) }) })