Skip to content

Commit

Permalink
fix: remove test coverage map (npm#4862)
Browse files Browse the repository at this point in the history
Turns out there were three files that still had no test coverage because
of the combination of the mocks in tests and the coverage map.  Removing
the map altogether exposed them.

This PR removes the coverage map and fixes test to cover all lines that
were being missed.

While adding coverage to the `npm search` codebase multiple unneeded
guards and at least one bug was found (it was impossible to exclude
searches based on username). These were fixed.

The `npm view` tests were also refactored to use the real npm object.

Finally, a small inlining of lib/utils/file-exists.js was done.
  • Loading branch information
wraithgar authored May 7, 2022
1 parent 8e7ea9b commit 48d2db6
Show file tree
Hide file tree
Showing 19 changed files with 729 additions and 861 deletions.
18 changes: 12 additions & 6 deletions lib/commands/completion.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,26 @@
// as an array.
//

const fs = require('@npmcli/fs')
const nopt = require('nopt')

const { definitions, shorthands } = require('../utils/config/index.js')
const { aliases, cmdList, plumbing } = require('../utils/cmd-list.js')
const aliasNames = Object.keys(aliases)
const fullList = cmdList.concat(aliasNames).filter(c => !plumbing.includes(c))
const nopt = require('nopt')
const configNames = Object.keys(definitions)
const shorthandNames = Object.keys(shorthands)
const allConfs = configNames.concat(shorthandNames)
const { isWindowsShell } = require('../utils/is-windows.js')
const fileExists = require('../utils/file-exists.js')
const fileExists = async (file) => {
try {
const stat = await fs.stat(file)
return stat.isFile()
} catch {
return false
}
}

const { promisify } = require('util')
const BaseCommand = require('../base-command.js')

class Completion extends BaseCommand {
Expand Down Expand Up @@ -189,12 +197,10 @@ class Completion extends BaseCommand {
}

const dumpScript = async () => {
const fs = require('fs')
const readFile = promisify(fs.readFile)
const { resolve } = require('path')
const p = resolve(__dirname, '..', 'utils', 'completion.sh')

const d = (await readFile(p, 'utf8')).replace(/^#!.*?\n/, '')
const d = (await fs.readFile(p, 'utf8')).replace(/^#!.*?\n/, '')
await new Promise((res, rej) => {
let done = false
process.stdout.on('error', er => {
Expand Down
47 changes: 27 additions & 20 deletions lib/commands/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,33 @@ const Pipeline = require('minipass-pipeline')
const libSearch = require('libnpmsearch')
const log = require('../utils/log-shim.js')

const formatPackageStream = require('../search/format-package-stream.js')
const packageFilter = require('../search/package-filter.js')
const formatSearchStream = require('../utils/format-search-stream.js')

function filter (data, include, exclude) {
const words = [data.name]
.concat(data.maintainers.map(m => `=${m.username}`))
.concat(data.keywords || [])
.map(f => f && f.trim && f.trim())
.filter(f => f)
.join(' ')
.toLowerCase()

if (exclude.find(e => match(words, e))) {
return false
}

function prepareIncludes (args) {
return args
.map(s => s.toLowerCase())
.filter(s => s)
return true
}

function prepareExcludes (searchexclude) {
var exclude
if (typeof searchexclude === 'string') {
exclude = searchexclude.split(/\s+/)
} else {
exclude = []
function match (words, pattern) {
if (pattern.startsWith('/')) {
if (pattern.endsWith('/')) {
pattern = pattern.slice(0, -1)
}
pattern = new RegExp(pattern.slice(1))
return words.match(pattern)
}

return exclude
.map(s => s.toLowerCase())
.filter(s => s)
return words.indexOf(pattern) !== -1
}

const BaseCommand = require('../base-command.js')
Expand Down Expand Up @@ -50,8 +57,8 @@ class Search extends BaseCommand {
const opts = {
...this.npm.flatOptions,
...this.npm.flatOptions.search,
include: prepareIncludes(args),
exclude: prepareExcludes(this.npm.flatOptions.search.exclude),
include: args.map(s => s.toLowerCase()).filter(s => s),
exclude: this.npm.flatOptions.search.exclude.split(/\s+/),
}

if (opts.include.length === 0) {
Expand All @@ -63,7 +70,7 @@ class Search extends BaseCommand {

class FilterStream extends Minipass {
write (pkg) {
if (packageFilter(pkg, opts.include, opts.exclude)) {
if (filter(pkg, opts.include, opts.exclude)) {
super.write(pkg)
}
}
Expand All @@ -73,7 +80,7 @@ class Search extends BaseCommand {

// Grab a configured output stream that will spit out packages in the
// desired format.
const outputStream = formatPackageStream({
const outputStream = formatSearchStream({
args, // --searchinclude options are not highlighted
...opts,
})
Expand Down
3 changes: 0 additions & 3 deletions lib/commands/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ class View extends BaseCommand {

function getFields (d, f, pref) {
f = f || []
if (!d) {
return f
}
pref = pref || []
Object.keys(d).forEach((k) => {
if (k.charAt(0) === '_' || k.indexOf('.') !== -1) {
Expand Down
43 changes: 0 additions & 43 deletions lib/search/package-filter.js

This file was deleted.

2 changes: 1 addition & 1 deletion lib/utils/config/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1856,7 +1856,7 @@ define('searchexclude', {
`,
flatten (key, obj, flatOptions) {
flatOptions.search = flatOptions.search || { limit: 20 }
flatOptions.search.exclude = obj[key]
flatOptions.search.exclude = obj[key].toLowerCase()
},
})

Expand Down
10 changes: 0 additions & 10 deletions lib/utils/file-exists.js

This file was deleted.

1 change: 1 addition & 0 deletions lib/utils/format-bytes.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const formatBytes = (bytes, space = true) => {
return `${(bytes / 1000000).toFixed(1)}${spacer}MB`
}

// GB
return `${(bytes / 1000000000).toFixed(1)}${spacer}GB`
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// XXX these output classes should not live in here forever. it'd be good to
// split them out, perhaps to libnpmsearch

const Minipass = require('minipass')
const columnify = require('columnify')

Expand All @@ -18,32 +15,26 @@ const columnify = require('columnify')
// The returned stream will format this package data
// into a byte stream of formatted, displayable output.

module.exports = (opts = {}) =>
opts.json ? new JSONOutputStream() : new TextOutputStream(opts)
module.exports = (opts) => {
return opts.json ? new JSONOutputStream() : new TextOutputStream(opts)
}

class JSONOutputStream extends Minipass {
constructor () {
super()
this._didFirst = false
}
#didFirst = false

write (obj) {
if (!this._didFirst) {
if (!this.#didFirst) {
super.write('[\n')
this._didFirst = true
this.#didFirst = true
} else {
super.write('\n,\n')
}

try {
return super.write(JSON.stringify(obj))
} catch (er) {
return this.emit('error', er)
}
return super.write(JSON.stringify(obj))
}

end () {
super.write(this._didFirst ? ']\n' : '\n[]\n')
super.write(this.#didFirst ? ']\n' : '\n[]\n')
super.end()
}
}
Expand All @@ -61,22 +52,22 @@ class TextOutputStream extends Minipass {
}

function prettify (data, num, opts) {
opts = opts || {}
var truncate = !opts.long

var pkg = normalizePackage(data, opts)

var columns = opts.description
? ['name', 'description', 'author', 'date', 'version', 'keywords']
: ['name', 'author', 'date', 'version', 'keywords']
var columns = ['name', 'description', 'author', 'date', 'version', 'keywords']

if (opts.parseable) {
return columns.map(function (col) {
return pkg[col] && ('' + pkg[col]).replace(/\t/g, ' ')
}).join('\t')
}

var output = columnify(
// stdout in tap is never a tty
/* istanbul ignore next */
const maxWidth = process.stdout.isTTY ? process.stdout.getWindowSize()[0] : Infinity
let output = columnify(
[pkg],
{
include: columns,
Expand All @@ -92,8 +83,8 @@ function prettify (data, num, opts) {
keywords: { maxWidth: Infinity },
},
}
)
output = trimToMaxWidth(output)
).split('\n').map(line => line.slice(0, maxWidth)).join('\n')

if (opts.color) {
output = highlightSearchTerms(output, opts.args)
}
Expand Down Expand Up @@ -140,26 +131,6 @@ function colorize (line) {
return line.split('\u0000').join(uncolor)
}

function getMaxWidth () {
var cols
try {
var tty = require('tty')
var stdout = process.stdout
cols = !tty.isatty(stdout.fd) ? Infinity : process.stdout.getWindowSize()[0]
cols = (cols === 0) ? Infinity : cols
} catch (ex) {
cols = Infinity
}
return cols
}

function trimToMaxWidth (str) {
var maxWidth = getMaxWidth()
return str.split('\n').map(function (line) {
return line.slice(0, maxWidth)
}).join('\n')
}

function highlightSearchTerms (str, terms) {
terms.forEach(function (arg, i) {
str = addColorMarker(str, arg, i)
Expand All @@ -169,13 +140,10 @@ function highlightSearchTerms (str, terms) {
}

function normalizePackage (data, opts) {
opts = opts || {}
return {
name: data.name,
description: opts.description ? data.description : '',
author: (data.maintainers || []).map(function (m) {
return '=' + m.username
}).join(' '),
description: data.description,
author: data.maintainers.map((m) => `=${m.username}`).join(' '),
keywords: Array.isArray(data.keywords)
? data.keywords.join(' ')
: typeof data.keywords === 'string'
Expand Down
9 changes: 0 additions & 9 deletions lib/utils/read-package-name.js

This file was deleted.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,13 @@
],
"color": 1,
"files": "test/{lib,bin,index.js}",
"coverage-map": "test/coverage-map.js",
"timeout": 600
"timeout": 600,
"nyc-arg": [
"--exclude",
"workspaces/**",
"--exclude",
"tap-snapshots/**"
]
},
"templateOSS": {
"rootRepo": false,
Expand Down
Loading

0 comments on commit 48d2db6

Please sign in to comment.