diff --git a/lib/utils/format-search-stream.js b/lib/utils/format-search-stream.js index 434d21f448acd..b70bd915123da 100644 --- a/lib/utils/format-search-stream.js +++ b/lib/utils/format-search-stream.js @@ -95,7 +95,7 @@ class TextOutputStream extends Minipass { // Normalize const pkg = { authors: data.maintainers.map((m) => `${strip(m.username)}`).join(' '), - publisher: strip(data.publisher.username), + publisher: strip(data.publisher?.username || ''), date: data.date ? data.date.toISOString().slice(0, 10) : 'prehistoric', description: strip(data.description ?? ''), keywords: [], @@ -159,7 +159,11 @@ class TextOutputStream extends Minipass { } else { output = `${name}\n` } - output += `Version ${this.#chalk.blue(pkg.version)} published ${this.#chalk.blue(pkg.date)} by ${this.#chalk.blue(pkg.publisher)}\n` + if (pkg.publisher) { + output += `Version ${this.#chalk.blue(pkg.version)} published ${this.#chalk.blue(pkg.date)} by ${this.#chalk.blue(pkg.publisher)}\n` + } else { + output += `Version ${this.#chalk.blue(pkg.version)} published ${this.#chalk.blue(pkg.date)} by ${this.#chalk.yellow('???')}\n` + } output += `Maintainers: ${pkg.authors}\n` if (keywords) { output += `Keywords: ${keywords}\n` diff --git a/tap-snapshots/test/lib/commands/search.js.test.cjs b/tap-snapshots/test/lib/commands/search.js.test.cjs index 3135964f202f5..7645e60abb88b 100644 --- a/tap-snapshots/test/lib/commands/search.js.test.cjs +++ b/tap-snapshots/test/lib/commands/search.js.test.cjs @@ -5,10 +5,6 @@ * Make sure to inspect the output below. Do not ignore changes! */ 'use strict' -exports[`test/lib/commands/search.js TAP empty search results > should have expected search results 1`] = ` -No matches found for "foo" -` - exports[`test/lib/commands/search.js TAP search //--color > should have expected search results with color 1`] = ` libnpm Collection of programmatic APIs for the npm CLI @@ -189,6 +185,10 @@ foo Version 1.0.0 published prehistoric by foo Maintainers: foo https://npm.im/foo +custom-registry +Version 1.0.0 published prehistoric by ??? +Maintainers: foo +https://npm.im/custom-registry libnpmversion Version 1.0.0 published prehistoric by foo Maintainers: foo @@ -274,6 +274,10 @@ Maintainers: lukekarrys https://npm.im/pkg-no-desc ` +exports[`test/lib/commands/search.js TAP search empty search results > should have expected search results 1`] = ` +No matches found for "foo" +` + exports[`test/lib/commands/search.js TAP search exclude forward slash > results should not have libnpmversion 1`] = ` libnpm Collection of programmatic APIs for the npm CLI @@ -1009,3 +1013,14 @@ Version 1.0.0 published 2019-09-26 by lukekarrys Maintainers: lukekarrys https://npm.im/pkg-no-desc ` + +exports[`test/lib/commands/search.js TAP search no publisher > should have filtered expected search results 1`] = ` +custom-registry +Version 1.0.0 published prehistoric by ??? +Maintainers: foo +https://npm.im/custom-registry +libnpmversion +Version 1.0.0 published prehistoric by foo +Maintainers: foo +https://npm.im/libnpmversion +` diff --git a/test/lib/commands/search.js b/test/lib/commands/search.js index fb0db8b21c5de..de4a58ca78a8f 100644 --- a/test/lib/commands/search.js +++ b/test/lib/commands/search.js @@ -4,222 +4,277 @@ const MockRegistry = require('@npmcli/mock-registry') const libnpmsearchResultFixture = require('../../fixtures/libnpmsearch-stream-result.js') -t.test('no args', async t => { - const { npm } = await loadMockNpm(t) - await t.rejects( - npm.exec('search', []), - /search must be called with arguments/, - 'should throw usage instructions' - ) -}) - -t.test('search text', async t => { - const { npm, joinedOutput } = await loadMockNpm(t) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), +t.test('search', t => { + t.test('no args', async t => { + const { npm } = await loadMockNpm(t) + await t.rejects( + npm.exec('search', []), + /search must be called with arguments/, + 'should throw usage instructions' + ) }) - registry.search({ results: libnpmsearchResultFixture }) - await npm.exec('search', ['libnpm']) - t.matchSnapshot(joinedOutput(), 'should have expected search results') -}) + t.test(' text', async t => { + const { npm, joinedOutput } = await loadMockNpm(t) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) -t.test('search --json', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { config: { json: true } }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + registry.search({ results: libnpmsearchResultFixture }) + await npm.exec('search', ['libnpm']) + t.matchSnapshot(joinedOutput(), 'should have expected search results') }) - registry.search({ results: libnpmsearchResultFixture }) + t.test(' --json', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { config: { json: true } }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) - await npm.exec('search', ['libnpm']) + registry.search({ results: libnpmsearchResultFixture }) - t.same( - JSON.parse(joinedOutput()), - libnpmsearchResultFixture, - 'should have expected search results as json' - ) -}) + await npm.exec('search', ['libnpm']) -t.test('search --parseable', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { config: { parseable: true } }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + t.same( + JSON.parse(joinedOutput()), + libnpmsearchResultFixture, + 'should have expected search results as json' + ) }) - registry.search({ results: libnpmsearchResultFixture }) - await npm.exec('search', ['libnpm']) - t.matchSnapshot(joinedOutput(), 'should have expected search results as parseable') -}) + t.test(' --parseable', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { config: { parseable: true } }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) -t.test('search --color', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { config: { color: 'always' } }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + registry.search({ results: libnpmsearchResultFixture }) + await npm.exec('search', ['libnpm']) + t.matchSnapshot(joinedOutput(), 'should have expected search results as parseable') }) - registry.search({ results: libnpmsearchResultFixture }) - await npm.exec('search', ['libnpm']) - t.matchSnapshot(joinedOutput(), 'should have expected search results with color') -}) + t.test(' --color', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { config: { color: 'always' } }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) -t.test('search //--color', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { config: { color: 'always' } }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + registry.search({ results: libnpmsearchResultFixture }) + await npm.exec('search', ['libnpm']) + t.matchSnapshot(joinedOutput(), 'should have expected search results with color') }) - registry.search({ results: libnpmsearchResultFixture }) - await npm.exec('search', ['/libnpm/']) - t.matchSnapshot(joinedOutput(), 'should have expected search results with color') -}) + t.test('//--color', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { config: { color: 'always' } }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) -t.test('search ', async t => { - const { npm, joinedOutput } = await loadMockNpm(t) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), - }) - - registry.search({ results: [{ - name: 'foo', - scope: 'unscoped', - version: '1.0.0', - description: '', - keywords: [], - date: null, - author: { name: 'Foo', email: 'foo@npmjs.com' }, - publisher: { username: 'foo', email: 'foo@npmjs.com' }, - maintainers: [ - { username: 'foo', email: 'foo@npmjs.com' }, - ], - }, { - name: 'libnpmversion', - scope: 'unscoped', - version: '1.0.0', - description: '', - keywords: [], - date: null, - author: { name: 'Foo', email: 'foo@npmjs.com' }, - publisher: { username: 'foo', email: 'foo@npmjs.com' }, - maintainers: [ - { username: 'foo', email: 'foo@npmjs.com' }, - ], - }] }) - - await npm.exec('search', ['foo']) - - t.matchSnapshot(joinedOutput(), 'should have filtered expected search results') -}) + registry.search({ results: libnpmsearchResultFixture }) + await npm.exec('search', ['/libnpm/']) + t.matchSnapshot(joinedOutput(), 'should have expected search results with color') + }) -t.test('empty search results', async t => { - const { npm, joinedOutput } = await loadMockNpm(t) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + t.test('', async t => { + const { npm, joinedOutput } = await loadMockNpm(t) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) + + registry.search({ results: [{ + name: 'foo', + scope: 'unscoped', + version: '1.0.0', + description: '', + keywords: [], + date: null, + author: { name: 'Foo', email: 'foo@npmjs.com' }, + publisher: { username: 'foo', email: 'foo@npmjs.com' }, + maintainers: [ + { username: 'foo', email: 'foo@npmjs.com' }, + ], + }, { + name: 'custom-registry', + scope: 'unscoped', + version: '1.0.0', + description: '', + keywords: [], + date: null, + author: { name: 'Foo', email: 'foo@npmjs.com' }, + maintainers: [ + { username: 'foo', email: 'foo@npmjs.com' }, + ], + }, { + name: 'libnpmversion', + scope: 'unscoped', + version: '1.0.0', + description: '', + keywords: [], + date: null, + author: { name: 'Foo', email: 'foo@npmjs.com' }, + publisher: { username: 'foo', email: 'foo@npmjs.com' }, + maintainers: [ + { username: 'foo', email: 'foo@npmjs.com' }, + ], + }] }) + + await npm.exec('search', ['foo']) + + t.matchSnapshot(joinedOutput(), 'should have filtered expected search results') }) - registry.search({ results: [] }) - await npm.exec('search', ['foo']) + t.test('no publisher', async t => { + const { npm, joinedOutput } = await loadMockNpm(t) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) + + registry.search({ results: [{ + name: 'custom-registry', + scope: 'unscoped', + version: '1.0.0', + description: '', + keywords: [], + date: null, + author: { name: 'Foo', email: 'foo@npmjs.com' }, + maintainers: [ + { username: 'foo', email: 'foo@npmjs.com' }, + ], + }, { + name: 'libnpmversion', + scope: 'unscoped', + version: '1.0.0', + description: '', + keywords: [], + date: null, + author: { name: 'Foo', email: 'foo@npmjs.com' }, + publisher: { username: 'foo', email: 'foo@npmjs.com' }, + maintainers: [ + { username: 'foo', email: 'foo@npmjs.com' }, + ], + }] }) + + await npm.exec('search', ['custom']) + + t.matchSnapshot(joinedOutput(), 'should have filtered expected search results') + }) - t.matchSnapshot(joinedOutput(), 'should have expected search results') -}) + t.test('empty search results', async t => { + const { npm, joinedOutput } = await loadMockNpm(t) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) -t.test('empty search results --json', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { config: { json: true } }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), - }) + registry.search({ results: [] }) + await npm.exec('search', ['foo']) - registry.search({ results: [] }) + t.matchSnapshot(joinedOutput(), 'should have expected search results') + }) - await npm.exec('search', ['foo']) - t.equal(joinedOutput(), '\n[]', 'should have expected empty square brackets') -}) + t.test('empty search results --json', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { config: { json: true } }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) -t.test('search api response error', async t => { - const { npm } = await loadMockNpm(t) + registry.search({ results: [] }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + await npm.exec('search', ['foo']) + t.equal(joinedOutput(), '\n[]', 'should have expected empty square brackets') }) - registry.search({ error: 'ERR' }) + t.test('api response error', async t => { + const { npm } = await loadMockNpm(t) - await t.rejects( - npm.exec('search', ['foo']), - /ERR/, - 'should throw response error' - ) -}) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) + + registry.search({ error: 'ERR' }) -t.test('search exclude string', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { config: { searchexclude: 'libnpmversion' } }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + await t.rejects( + npm.exec('search', ['foo']), + /ERR/, + 'should throw response error' + ) }) - registry.search({ results: libnpmsearchResultFixture }) - await npm.exec('search', ['libnpm']) - t.matchSnapshot(joinedOutput(), 'results should not have libnpmversion') -}) -t.test('search exclude string json', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { - config: { - json: true, - searchexclude: 'libnpmversion', - }, + t.test('exclude string', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + config: { + searchexclude: 'libnpmversion', + }, + }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) + + registry.search({ results: libnpmsearchResultFixture }) + await npm.exec('search', ['libnpm']) + t.matchSnapshot(joinedOutput(), 'results should not have libnpmversion') }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + t.test('exclude string json', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + config: { + json: true, + searchexclude: 'libnpmversion', + }, + }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) + + registry.search({ results: libnpmsearchResultFixture }) + await npm.exec('search', ['libnpm']) + t.matchSnapshot(JSON.parse(joinedOutput()), 'results should not have libnpmversion') }) - registry.search({ results: libnpmsearchResultFixture }) - await npm.exec('search', ['libnpm']) - t.matchSnapshot(JSON.parse(joinedOutput()), 'results should not have libnpmversion') -}) + t.test('exclude username with upper case letters', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { config: { searchexclude: 'NLF' } }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) -t.test('search exclude username with upper case letters', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { config: { searchexclude: 'NLF' } }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + registry.search({ results: libnpmsearchResultFixture }) + await npm.exec('search', ['libnpm']) + t.matchSnapshot(joinedOutput(), 'results should not have nlf') }) - registry.search({ results: libnpmsearchResultFixture }) - await npm.exec('search', ['libnpm']) - t.matchSnapshot(joinedOutput(), 'results should not have nlf') -}) + t.test('exclude regex', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { config: { searchexclude: '/version/' } }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) -t.test('search exclude regex', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { config: { searchexclude: '/version/' } }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + registry.search({ results: libnpmsearchResultFixture }) + await npm.exec('search', ['libnpm']) + t.matchSnapshot(joinedOutput(), 'results should not have libnpmversion') }) - registry.search({ results: libnpmsearchResultFixture }) - await npm.exec('search', ['libnpm']) - t.matchSnapshot(joinedOutput(), 'results should not have libnpmversion') -}) + t.test('exclude forward slash', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { config: { searchexclude: '/version' } }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + }) -t.test('search exclude forward slash', async t => { - const { npm, joinedOutput } = await loadMockNpm(t, { config: { searchexclude: '/version' } }) - const registry = new MockRegistry({ - tap: t, - registry: npm.config.get('registry'), + registry.search({ results: libnpmsearchResultFixture }) + await npm.exec('search', ['libnpm']) + t.matchSnapshot(joinedOutput(), 'results should not have libnpmversion') }) - - registry.search({ results: libnpmsearchResultFixture }) - await npm.exec('search', ['libnpm']) - t.matchSnapshot(joinedOutput(), 'results should not have libnpmversion') + t.end() })