Skip to content

Commit

Permalink
tests and minor fix for help-search command
Browse files Browse the repository at this point in the history
PR-URL: #2347
Credit: @nlf
Close: #2347
Reviewed-by: @ruyadorno
  • Loading branch information
nlf committed Dec 15, 2020
1 parent 85c2a2d commit 3ba5de4
Show file tree
Hide file tree
Showing 2 changed files with 191 additions and 17 deletions.
27 changes: 10 additions & 17 deletions lib/help-search.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
const fs = require('fs')
const path = require('path')
const npm = require('./npm.js')
const glob = require('glob')
const color = require('ansicolors')
const output = require('./utils/output.js')
const usageUtil = require('./utils/usage.js')
const { promisify } = require('util')
const glob = promisify(require('glob'))
const readFile = promisify(fs.readFile)
const didYouMean = require('./utils/did-you-mean.js')
const { cmdList } = require('./utils/cmd-list.js')
Expand All @@ -23,12 +23,17 @@ const helpSearch = async args => {

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

// XXX: make glob return a promise and remove this wrapping
const files = await new Promise((res, rej) =>
glob(`${docPath}/*/*.md`, (er, files) => er ? rej(er) : res(files)))

const files = await glob(`${docPath}/*/*.md`)
const data = await readFiles(files)
const results = await searchFiles(args, data, files)
// if only one result, then just show that help section.
if (results.length === 1) {
return npm.commands.help([path.basename(results[0].file, '.md')], er => {
if (er)
throw er
})
}

const formatted = formatResults(args, results)
if (!formatted.trim())
npmUsage(false)
Expand Down Expand Up @@ -125,15 +130,6 @@ const searchFiles = async (args, data, files) => {
})
}

// if only one result, then just show that help section.
if (results.length === 1) {
npm.commands.help([results[0].file.replace(/\.md$/, '')], er => {
if (er)
throw er
})
return []
}

// sort results by number of results found, then by number of hits
// then by number of matching lines
return results.sort((a, b) =>
Expand All @@ -147,9 +143,6 @@ const searchFiles = async (args, data, files) => {
}

const formatResults = (args, results) => {
if (!results)
return 'No results for ' + args.map(JSON.stringify).join(' ')

const cols = Math.min(process.stdout.columns || Infinity, 80) + 1

const out = results.map(res => {
Expand Down
181 changes: 181 additions & 0 deletions test/lib/help-search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
const { test } = require('tap')
const { join } = require('path')
const requireInject = require('require-inject')
const ansicolors = require('ansicolors')

const OUTPUT = []
const output = (msg) => {
OUTPUT.push(msg)
}

let npmHelpArgs = null
let npmHelpErr = null
const npm = {
color: false,
flatOptions: {
long: false,
},
commands: {
help: (args, cb) => {
npmHelpArgs = args
return cb(npmHelpErr)
},
},
}

let npmUsageArg = null
const npmUsage = (arg) => {
npmUsageArg = arg
}

let globRoot = null
const globDir = {
'npm-exec.md': 'the exec command\nhelp has multiple lines of exec help\none of them references exec',
'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',
'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',
'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',
'npm-help.md': 'will run the `help-search` command if you need to run it to help you search',
'npm-help-search.md': 'is the help search command\nthat you get if you run help-search',
'npm-useless.md': 'exec\nexec',
'npm-more-useless.md': 'exec exec',
'npm-extra-useless.md': 'exec\nexec\nexec',
}
const glob = (p, cb) => cb(null, Object.keys(globDir).map((file) => join(globRoot, file)))

const helpSearch = requireInject('../../lib/help-search.js', {
'../../lib/npm.js': npm,
'../../lib/utils/npm-usage.js': npmUsage,
'../../lib/utils/output.js': output,
glob,
})

test('npm help-search', t => {
globRoot = t.testdir(globDir)
t.teardown(() => {
OUTPUT.length = 0
globRoot = null
})

return helpSearch(['exec'], (err) => {
if (err)
throw err

t.match(OUTPUT, /Top hits for/, 'outputs results')
t.match(OUTPUT, /Did you mean this\?\n\s+exec/, 'matched command, so suggest it')
t.end()
})
})

test('npm help-search multiple terms', t => {
globRoot = t.testdir(globDir)
t.teardown(() => {
OUTPUT.length = 0
globRoot = null
})

return helpSearch(['run', 'script'], (err) => {
if (err)
throw err

t.match(OUTPUT, /Top hits for/, 'outputs results')
t.match(OUTPUT, /run:\d+ script:\d+/, 'shows hit counts for both terms')
t.end()
})
})

test('npm help-search single result prints full section', t => {
globRoot = t.testdir(globDir)
t.teardown(() => {
OUTPUT.length = 0
npmHelpArgs = null
globRoot = null
})

return helpSearch(['does not exist in'], (err) => {
if (err)
throw err

t.strictSame(npmHelpArgs, ['npm-install'], 'identified the correct man page and called help with it')
t.end()
})
})

test('npm help-search single result propagates error', t => {
globRoot = t.testdir(globDir)
npmHelpErr = new Error('help broke')
t.teardown(() => {
OUTPUT.length = 0
npmHelpArgs = null
npmHelpErr = null
globRoot = null
})

return helpSearch(['does not exist in'], (err) => {
t.strictSame(npmHelpArgs, ['npm-install'], 'identified the correct man page and called help with it')
t.match(err, /help broke/, 'propagated the error from help')
t.end()
})
})

test('npm help-search long output', t => {
globRoot = t.testdir(globDir)
npm.flatOptions.long = true
t.teardown(() => {
OUTPUT.length = 0
npm.flatOptions.long = false
globRoot = null
})

return helpSearch(['exec'], (err) => {
if (err)
throw err

t.match(OUTPUT, /has multiple lines of exec help/, 'outputs detailed results')
t.end()
})
})

test('npm help-search long output with color', t => {
globRoot = t.testdir(globDir)
npm.flatOptions.long = true
npm.color = true
t.teardown(() => {
OUTPUT.length = 0
npm.flatOptions.long = false
npm.color = false
globRoot = null
})

return helpSearch(['help-search'], (err) => {
if (err)
throw err

const highlightedText = ansicolors.bgBlack(ansicolors.red('help-search'))
t.equal(OUTPUT.some((line) => line.includes(highlightedText)), true, 'returned highlighted search terms')
t.end()
})
})

test('npm help-search no args', t => {
return helpSearch([], (err) => {
t.match(err, /npm help-search/, 'throws usage')
t.end()
})
})

test('npm help-search no matches', t => {
globRoot = t.testdir(globDir)
t.teardown(() => {
OUTPUT.length = 0
npmUsageArg = null
globRoot = null
})

return helpSearch(['asdfasdf'], (err) => {
if (err)
throw err

t.equal(npmUsageArg, false, 'called npmUsage for no matches')
t.end()
})
})

0 comments on commit 3ba5de4

Please sign in to comment.