1'use strict' 2 3const cacheFile = require('npm-cache-filename') 4const mkdirp = require('mkdirp') 5const mr = require('npm-registry-mock') 6const path = require('path') 7const qs = require('querystring') 8const Tacks = require('tacks') 9const test = require('tap').test 10 11const {File} = Tacks 12 13const common = require('../common-tap.js') 14 15const CACHE_DIR = common.cache 16const cacheBase = cacheFile(CACHE_DIR)(common.registry + '/-/all') 17const cachePath = path.join(cacheBase, '.cache.json') 18const chownr = require('chownr') 19const fixOwner = ( 20 process.getuid && process.getuid() === 0 && 21 process.env.SUDO_UID && process.env.SUDO_GID 22) ? (path) => chownr.sync(path, +process.env.SUDO_UID, +process.env.SUDO_GID) 23 : () => {} 24 25let server 26 27test('setup', function (t) { 28 mkdirp.sync(cacheBase) 29 fixOwner(CACHE_DIR) 30 mr({port: common.port, throwOnUnmatched: true}, function (err, s) { 31 t.ifError(err, 'registry mocked successfully') 32 server = s 33 t.pass('all set up') 34 t.done() 35 }) 36}) 37 38const searches = [ 39 { 40 term: 'cool', 41 description: 'non-regex search', 42 location: 103 43 }, 44 { 45 term: '/cool/', 46 description: 'regex search', 47 location: 103 48 }, 49 { 50 term: 'cool', 51 description: 'searches name field', 52 location: 103 53 }, 54 { 55 term: 'ool', 56 description: 'excludes matches for --searchexclude', 57 location: 205, 58 inject: { 59 other: { name: 'other', description: 'this is a simple tool' } 60 }, 61 extraOpts: ['--searchexclude', 'cool'] 62 }, 63 { 64 term: 'neat lib', 65 description: 'searches description field', 66 location: 141, 67 inject: { 68 cool: { 69 name: 'cool', version: '5.0.0', description: 'this is a neat lib' 70 } 71 } 72 }, 73 { 74 term: 'foo', 75 description: 'skips description field with --no-description', 76 location: 80, 77 inject: { 78 cool: { 79 name: 'cool', version: '5.0.0', description: 'foo bar!' 80 } 81 }, 82 extraOpts: ['--no-description'] 83 }, 84 { 85 term: 'zkat', 86 description: 'searches maintainers by name', 87 location: 155, 88 inject: { 89 cool: { 90 name: 'cool', 91 version: '5.0.0', 92 maintainers: [{ 93 name: 'zkat' 94 }] 95 } 96 } 97 }, 98 { 99 term: '=zkat', 100 description: 'searches maintainers unambiguously by =name', 101 location: 154, 102 inject: { 103 bar: { name: 'bar', description: 'zkat thing', version: '1.0.0' }, 104 cool: { 105 name: 'cool', 106 version: '5.0.0', 107 maintainers: [{ 108 name: 'zkat' 109 }] 110 } 111 } 112 }, 113 { 114 term: 'github.com', 115 description: 'searches projects by url', 116 location: 205, 117 inject: { 118 bar: { 119 name: 'bar', 120 url: 'gitlab.com/bar', 121 // For historical reasons, `url` is only present if `versions` is there 122 versions: ['1.0.0'], 123 version: '1.0.0' 124 }, 125 cool: { 126 name: 'cool', 127 version: '5.0.0', 128 versions: ['1.0.0'], 129 url: 'github.com/cool/cool' 130 } 131 } 132 }, 133 { 134 term: 'monad', 135 description: 'searches projects by keywords', 136 location: 197, 137 inject: { 138 cool: { 139 name: 'cool', 140 version: '5.0.0', 141 keywords: ['monads'] 142 } 143 } 144 } 145] 146 147// These test classic hand-matched searches 148searches.forEach(function (search) { 149 test(search.description, function (t) { 150 const query = qs.stringify({ 151 text: search.term, 152 size: 20, 153 from: 0, 154 quality: 0.65, 155 popularity: 0.98, 156 maintenance: 0.5 157 }) 158 server.get(`/-/v1/search?${query}`).once().reply(404, {}) 159 const now = Date.now() 160 const cacheContents = { 161 '_updated': now, 162 bar: { name: 'bar', version: '1.0.0' }, 163 cool: { name: 'cool', version: '5.0.0' }, 164 foo: { name: 'foo', version: '2.0.0' }, 165 other: { name: 'other', version: '1.0.0' } 166 } 167 for (let k in search.inject) { 168 cacheContents[k] = search.inject[k] 169 } 170 const fixture = new Tacks(File(cacheContents)) 171 fixture.create(cachePath) 172 fixOwner(cachePath) 173 common.npm([ 174 'search', search.term, 175 '--registry', common.registry, 176 '--cache', CACHE_DIR, 177 '--loglevel', 'error', 178 '--color', 'always' 179 ].concat(search.extraOpts || []), 180 {}, 181 function (err, code, stdout, stderr) { 182 t.equal(stderr, '', 'no error output') 183 t.notEqual(stdout, '', 'got output') 184 t.equal(code, 0, 'search finished successfully') 185 t.ifErr(err, 'search finished successfully') 186 // \033 == \u001B 187 const markStart = '\u001B\\[[0-9][0-9]m' 188 const markEnd = '\u001B\\[0m' 189 190 const re = new RegExp(markStart + '.*?' + markEnd) 191 192 const cnt = stdout.search(re) 193 t.equal( 194 cnt, 195 search.location, 196 search.description + ' search for ' + search.term 197 ) 198 t.end() 199 }) 200 }) 201}) 202 203test('cleanup', function (t) { 204 server.done() 205 server.close() 206 t.end() 207}) 208