• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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