Skip to content

Commit 3ba5de4

Browse files
committed
tests and minor fix for help-search command
PR-URL: #2347 Credit: @nlf Close: #2347 Reviewed-by: @ruyadorno
1 parent 85c2a2d commit 3ba5de4

File tree

2 files changed

+191
-17
lines changed

2 files changed

+191
-17
lines changed

lib/help-search.js

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
const fs = require('fs')
22
const path = require('path')
33
const npm = require('./npm.js')
4-
const glob = require('glob')
54
const color = require('ansicolors')
65
const output = require('./utils/output.js')
76
const usageUtil = require('./utils/usage.js')
87
const { promisify } = require('util')
8+
const glob = promisify(require('glob'))
99
const readFile = promisify(fs.readFile)
1010
const didYouMean = require('./utils/did-you-mean.js')
1111
const { cmdList } = require('./utils/cmd-list.js')
@@ -23,12 +23,17 @@ const helpSearch = async args => {
2323

2424
const docPath = path.resolve(__dirname, '..', 'docs/content')
2525

26-
// XXX: make glob return a promise and remove this wrapping
27-
const files = await new Promise((res, rej) =>
28-
glob(`${docPath}/*/*.md`, (er, files) => er ? rej(er) : res(files)))
29-
26+
const files = await glob(`${docPath}/*/*.md`)
3027
const data = await readFiles(files)
3128
const results = await searchFiles(args, data, files)
29+
// if only one result, then just show that help section.
30+
if (results.length === 1) {
31+
return npm.commands.help([path.basename(results[0].file, '.md')], er => {
32+
if (er)
33+
throw er
34+
})
35+
}
36+
3237
const formatted = formatResults(args, results)
3338
if (!formatted.trim())
3439
npmUsage(false)
@@ -125,15 +130,6 @@ const searchFiles = async (args, data, files) => {
125130
})
126131
}
127132

128-
// if only one result, then just show that help section.
129-
if (results.length === 1) {
130-
npm.commands.help([results[0].file.replace(/\.md$/, '')], er => {
131-
if (er)
132-
throw er
133-
})
134-
return []
135-
}
136-
137133
// sort results by number of results found, then by number of hits
138134
// then by number of matching lines
139135
return results.sort((a, b) =>
@@ -147,9 +143,6 @@ const searchFiles = async (args, data, files) => {
147143
}
148144

149145
const formatResults = (args, results) => {
150-
if (!results)
151-
return 'No results for ' + args.map(JSON.stringify).join(' ')
152-
153146
const cols = Math.min(process.stdout.columns || Infinity, 80) + 1
154147

155148
const out = results.map(res => {

test/lib/help-search.js

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
const { test } = require('tap')
2+
const { join } = require('path')
3+
const requireInject = require('require-inject')
4+
const ansicolors = require('ansicolors')
5+
6+
const OUTPUT = []
7+
const output = (msg) => {
8+
OUTPUT.push(msg)
9+
}
10+
11+
let npmHelpArgs = null
12+
let npmHelpErr = null
13+
const npm = {
14+
color: false,
15+
flatOptions: {
16+
long: false,
17+
},
18+
commands: {
19+
help: (args, cb) => {
20+
npmHelpArgs = args
21+
return cb(npmHelpErr)
22+
},
23+
},
24+
}
25+
26+
let npmUsageArg = null
27+
const npmUsage = (arg) => {
28+
npmUsageArg = arg
29+
}
30+
31+
let globRoot = null
32+
const globDir = {
33+
'npm-exec.md': 'the exec command\nhelp has multiple lines of exec help\none of them references exec',
34+
'npm-something.md': 'another\ncommand you run\nthat\nreferences exec\nand has multiple lines\nwith no matches\nthat will be ignored\nand another line\nthat does have exec as well',
35+
'npm-run-script.md': 'the scripted run-script command runs scripts\nand has lines\nsome of which dont match the string run\nor script\nscript',
36+
'npm-install.md': 'does a thing in a script\nif a thing does not exist in a thing you run\nto install it and run it maybe in a script',
37+
'npm-help.md': 'will run the `help-search` command if you need to run it to help you search',
38+
'npm-help-search.md': 'is the help search command\nthat you get if you run help-search',
39+
'npm-useless.md': 'exec\nexec',
40+
'npm-more-useless.md': 'exec exec',
41+
'npm-extra-useless.md': 'exec\nexec\nexec',
42+
}
43+
const glob = (p, cb) => cb(null, Object.keys(globDir).map((file) => join(globRoot, file)))
44+
45+
const helpSearch = requireInject('../../lib/help-search.js', {
46+
'../../lib/npm.js': npm,
47+
'../../lib/utils/npm-usage.js': npmUsage,
48+
'../../lib/utils/output.js': output,
49+
glob,
50+
})
51+
52+
test('npm help-search', t => {
53+
globRoot = t.testdir(globDir)
54+
t.teardown(() => {
55+
OUTPUT.length = 0
56+
globRoot = null
57+
})
58+
59+
return helpSearch(['exec'], (err) => {
60+
if (err)
61+
throw err
62+
63+
t.match(OUTPUT, /Top hits for/, 'outputs results')
64+
t.match(OUTPUT, /Did you mean this\?\n\s+exec/, 'matched command, so suggest it')
65+
t.end()
66+
})
67+
})
68+
69+
test('npm help-search multiple terms', t => {
70+
globRoot = t.testdir(globDir)
71+
t.teardown(() => {
72+
OUTPUT.length = 0
73+
globRoot = null
74+
})
75+
76+
return helpSearch(['run', 'script'], (err) => {
77+
if (err)
78+
throw err
79+
80+
t.match(OUTPUT, /Top hits for/, 'outputs results')
81+
t.match(OUTPUT, /run:\d+ script:\d+/, 'shows hit counts for both terms')
82+
t.end()
83+
})
84+
})
85+
86+
test('npm help-search single result prints full section', t => {
87+
globRoot = t.testdir(globDir)
88+
t.teardown(() => {
89+
OUTPUT.length = 0
90+
npmHelpArgs = null
91+
globRoot = null
92+
})
93+
94+
return helpSearch(['does not exist in'], (err) => {
95+
if (err)
96+
throw err
97+
98+
t.strictSame(npmHelpArgs, ['npm-install'], 'identified the correct man page and called help with it')
99+
t.end()
100+
})
101+
})
102+
103+
test('npm help-search single result propagates error', t => {
104+
globRoot = t.testdir(globDir)
105+
npmHelpErr = new Error('help broke')
106+
t.teardown(() => {
107+
OUTPUT.length = 0
108+
npmHelpArgs = null
109+
npmHelpErr = null
110+
globRoot = null
111+
})
112+
113+
return helpSearch(['does not exist in'], (err) => {
114+
t.strictSame(npmHelpArgs, ['npm-install'], 'identified the correct man page and called help with it')
115+
t.match(err, /help broke/, 'propagated the error from help')
116+
t.end()
117+
})
118+
})
119+
120+
test('npm help-search long output', t => {
121+
globRoot = t.testdir(globDir)
122+
npm.flatOptions.long = true
123+
t.teardown(() => {
124+
OUTPUT.length = 0
125+
npm.flatOptions.long = false
126+
globRoot = null
127+
})
128+
129+
return helpSearch(['exec'], (err) => {
130+
if (err)
131+
throw err
132+
133+
t.match(OUTPUT, /has multiple lines of exec help/, 'outputs detailed results')
134+
t.end()
135+
})
136+
})
137+
138+
test('npm help-search long output with color', t => {
139+
globRoot = t.testdir(globDir)
140+
npm.flatOptions.long = true
141+
npm.color = true
142+
t.teardown(() => {
143+
OUTPUT.length = 0
144+
npm.flatOptions.long = false
145+
npm.color = false
146+
globRoot = null
147+
})
148+
149+
return helpSearch(['help-search'], (err) => {
150+
if (err)
151+
throw err
152+
153+
const highlightedText = ansicolors.bgBlack(ansicolors.red('help-search'))
154+
t.equal(OUTPUT.some((line) => line.includes(highlightedText)), true, 'returned highlighted search terms')
155+
t.end()
156+
})
157+
})
158+
159+
test('npm help-search no args', t => {
160+
return helpSearch([], (err) => {
161+
t.match(err, /npm help-search/, 'throws usage')
162+
t.end()
163+
})
164+
})
165+
166+
test('npm help-search no matches', t => {
167+
globRoot = t.testdir(globDir)
168+
t.teardown(() => {
169+
OUTPUT.length = 0
170+
npmUsageArg = null
171+
globRoot = null
172+
})
173+
174+
return helpSearch(['asdfasdf'], (err) => {
175+
if (err)
176+
throw err
177+
178+
t.equal(npmUsageArg, false, 'called npmUsage for no matches')
179+
t.end()
180+
})
181+
})

0 commit comments

Comments
 (0)