const t = require('tap') const requireInject = require('require-inject') let logs const cleanLogs = (done) => { logs = '' const fn = (...args) => { logs += '\n' args.map(el => logs += el) } console.log = fn done() } const packument = (nv, opts) => { if (!opts.fullMetadata) throw new Error('must fetch fullMetadata') if (!opts.preferOnline) throw new Error('must fetch with preferOnline') const mocks = { red: { name: 'red', 'dist-tags': { '1.0.1': {}, }, time: { unpublished: new Date(), }, }, blue: { name: 'blue', 'dist-tags': {}, time: { '1.0.0': '2019-08-06T16:21:09.842Z', }, versions: { '1.0.0': { name: 'blue', version: '1.0.0', dist: { shasum: '123', tarball: 'http://hm.blue.com/1.0.0.tgz', integrity: '---', fileCount: 1, unpackedSize: 1, }, }, '1.0.1': {}, }, }, cyan: { _npmUser: { name: 'claudia', email: 'claudia@cyan.com', }, name: 'cyan', 'dist-tags': {}, versions: { '1.0.0': { version: '1.0.0', name: 'cyan', dist: { shasum: '123', tarball: 'http://hm.cyan.com/1.0.0.tgz', integrity: '---', fileCount: 1, unpackedSize: 1, }, }, '1.0.1': {}, }, }, brown: { name: 'brown', }, yellow: { _id: 'yellow', name: 'yellow', author: { name: 'foo', email: 'foo@yellow.com', twitter: 'foo', }, readme: 'a very useful readme', versions: { '1.0.0': { version: '1.0.0', author: 'claudia', readme: 'a very useful readme', maintainers: [ { name: 'claudia', email: 'c@yellow.com', twitter: 'cyellow' }, { name: 'isaacs', email: 'i@yellow.com', twitter: 'iyellow' }, ], }, '1.0.1': { version: '1.0.1', author: 'claudia', }, '1.0.2': { version: '1.0.2', author: 'claudia', }, }, }, purple: { name: 'purple', versions: { '1.0.0': { foo: 1, maintainers: [ { name: 'claudia' }, ], }, '1.0.1': {}, }, }, green: { _id: 'green', name: 'green', 'dist-tags': { latest: '1.0.0', }, maintainers: [ { name: 'claudia', email: 'c@yellow.com', twitter: 'cyellow' }, { name: 'isaacs', email: 'i@yellow.com', twitter: 'iyellow' }, ], keywords: ['colors', 'green', 'crayola'], versions: { '1.0.0': { _id: 'green', version: '1.0.0', description: 'green is a very important color', bugs: { url: 'http://bugs.green.com', }, deprecated: true, repository: { url: 'http://repository.green.com', }, license: { type: 'ACME' }, bin: { green: 'bin/green.js', }, dependencies: { red: '1.0.0', yellow: '1.0.0', }, dist: { shasum: '123', tarball: 'http://hm.green.com/1.0.0.tgz', integrity: '---', fileCount: 1, unpackedSize: 1, }, }, '1.0.1': {}, }, }, black: { name: 'black', 'dist-tags': { latest: '1.0.0', }, versions: { '1.0.0': { version: '1.0.0', bugs: 'http://bugs.black.com', license: {}, dependencies: (() => { const deps = {} for (let i = 0; i < 25; i++) deps[i] = '1.0.0' return deps })(), dist: { shasum: '123', tarball: 'http://hm.black.com/1.0.0.tgz', integrity: '---', fileCount: 1, unpackedSize: 1, }, }, '1.0.1': {}, }, }, pink: { name: 'pink', 'dist-tags': { latest: '1.0.0', }, versions: { '1.0.0': { version: '1.0.0', maintainers: [ { name: 'claudia', url: 'http://c.pink.com' }, { name: 'isaacs', url: 'http://i.pink.com' }, ], repository: 'http://repository.pink.com', license: {}, dist: { shasum: '123', tarball: 'http://hm.pink.com/1.0.0.tgz', integrity: '---', fileCount: 1, unpackedSize: 1, }, }, '1.0.1': {}, }, }, orange: { name: 'orange', 'dist-tags': { latest: '1.0.0', }, versions: { '1.0.0': { version: '1.0.0', homepage: 'http://hm.orange.com', license: {}, dist: { shasum: '123', tarball: 'http://hm.orange.com/1.0.0.tgz', integrity: '---', fileCount: 1, unpackedSize: 1, }, }, '1.0.1': {}, }, }, } return mocks[nv.name] } t.beforeEach(cleanLogs) t.test('should log package info', t => { const View = requireInject('../../lib/view.js', { pacote: { packument, }, }) const view = new View({ flatOptions: { global: false, }, }) const ViewJson = requireInject('../../lib/view.js', { pacote: { packument, }, }) const viewJson = new ViewJson({ flatOptions: { json: true, }, }) const ViewUnicode = requireInject('../../lib/view.js', { pacote: { packument, }, }) const viewUnicode = new ViewUnicode({ flatOptions: { global: false, unicode: true, }, }) t.test('package with license, bugs, repository and other fields', t => { view.exec(['green@1.0.0'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('package with more than 25 deps', t => { view.exec(['black@1.0.0'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('package with maintainers info as object', t => { view.exec(['pink@1.0.0'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('package with homepage', t => { view.exec(['orange@1.0.0'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('package with no versions', t => { view.exec(['brown'], () => { t.equals(logs, '', 'no info to display') t.end() }) }) t.test('package with no repo or homepage', t => { view.exec(['blue@1.0.0'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('package with no modified time', t => { viewUnicode.exec(['cyan@1.0.0'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('package with --json and semver range', t => { viewJson.exec(['cyan@^1.0.0'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('package with --json and no versions', t => { viewJson.exec(['brown'], () => { t.equals(logs, '', 'no info to display') t.end() }) }) t.end() }) t.test('should log info of package in current working dir', t => { const testDir = t.testdir({ 'package.json': JSON.stringify({ name: 'blue', version: '1.0.0', }, null, 2), }) const View = requireInject('../../lib/view.js', { pacote: { packument, }, }) const view = new View({ prefix: testDir, flatOptions: { defaultTag: '1.0.0', global: false, }, }) t.test('specific version', t => { view.exec(['.@1.0.0'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('non-specific version', t => { view.exec(['.'], () => { t.matchSnapshot(logs) t.end() }) }) t.end() }) t.test('should log info by field name', t => { const ViewJson = requireInject('../../lib/view.js', { pacote: { packument, }, }) const viewJson = new ViewJson({ flatOptions: { json: true, global: false, }, }) const View = requireInject('../../lib/view.js', { pacote: { packument, }, }) const view = new View({ flatOptions: { global: false, }, }) t.test('readme', t => { view.exec(['yellow@1.0.0', 'readme'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('several fields', t => { viewJson.exec(['yellow@1.0.0', 'name', 'version', 'foo[bar]'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('several fields with several versions', t => { view.exec(['yellow@1.x.x', 'author'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('nested field with brackets', t => { viewJson.exec(['orange@1.0.0', 'dist[shasum]'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('maintainers with email', t => { viewJson.exec(['yellow@1.0.0', 'maintainers', 'name'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('maintainers with url', t => { viewJson.exec(['pink@1.0.0', 'maintainers'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('unknown nested field ', t => { view.exec(['yellow@1.0.0', 'dist.foobar'], () => { t.equals(logs, '', 'no info to display') t.end() }) }) t.test('array field - 1 element', t => { view.exec(['purple@1.0.0', 'maintainers.name'], () => { t.matchSnapshot(logs) t.end() }) }) t.test('array field - 2 elements', t => { view.exec(['yellow@1.x.x', 'maintainers.name'], () => { t.matchSnapshot(logs) t.end() }) }) t.end() }) t.test('throw error if global mode', (t) => { const View = requireInject('../../lib/view.js') const view = new View({ flatOptions: { global: true, }, }) view.exec([], (err) => { t.equals(err.message, 'Cannot use view command in global mode.') t.end() }) }) t.test('throw ENOENT error if package.json misisng', (t) => { const testDir = t.testdir({}) const View = requireInject('../../lib/view.js') const view = new View({ prefix: testDir, flatOptions: { global: false, }, }) view.exec([], (err) => { t.match(err, { code: 'ENOENT' }) t.end() }) }) t.test('throw EJSONPARSE error if package.json not json', (t) => { const testDir = t.testdir({ 'package.json': 'not json, nope, not even a little bit!', }) const View = requireInject('../../lib/view.js') const view = new View({ prefix: testDir, flatOptions: { global: false, }, }) view.exec([], (err) => { t.match(err, { code: 'EJSONPARSE' }) t.end() }) }) t.test('throw error if package.json has no name', (t) => { const testDir = t.testdir({ 'package.json': '{}', }) const View = requireInject('../../lib/view.js') const view = new View({ prefix: testDir, flatOptions: { global: false, }, }) view.exec([], (err) => { t.equals(err.message, 'Invalid package.json, no "name" field') t.end() }) }) t.test('throws when unpublished', (t) => { const View = requireInject('../../lib/view.js', { pacote: { packument, }, }) const view = new View({ flatOptions: { defaultTag: '1.0.1', global: false, }, }) view.exec(['red'], (err) => { t.equals(err.code, 'E404') t.end() }) }) t.test('completion', async t => { const View = requireInject('../../lib/view.js', { pacote: { packument, }, }) const view = new View({ flatOptions: { defaultTag: '1.0.1', global: false, }, }) const res = await view.completion({ conf: { argv: { remain: ['npm', 'view', 'green@1.0.0'] } }, }) t.ok(res, 'returns back fields') t.end() }) t.test('no registry completion', async t => { const View = requireInject('../../lib/view.js') const view = new View({ flatOptions: { defaultTag: '1.0.1', }, }) const res = await view.completion({conf: { argv: { remain: ['npm', 'view'] } } }) t.notOk(res, 'there is no package completion') t.end() })