1// Our coverage mapping means that stuff like this doen't count for coverage. 2// It does ensure that every command has a usage that renders, contains its 3// name, a description, and if it has completion it is a function. That it 4// renders also ensures that any params we've defined in our commands work. 5const t = require('tap') 6const util = require('util') 7const { load: loadMockNpm } = require('../fixtures/mock-npm.js') 8const { commands } = require('../../lib/utils/cmd-list.js') 9const BaseCommand = require('../../lib/base-command.js') 10 11const isAsyncFn = (v) => typeof v === 'function' && /^\[AsyncFunction:/.test(util.inspect(v)) 12 13t.test('load each command', async t => { 14 const counts = { 15 completion: 0, 16 ignoreImplicitWorkspace: 0, 17 workspaces: 0, 18 noParams: 0, 19 } 20 21 for (const cmd of commands) { 22 await t.test(cmd, async t => { 23 const { npm, outputs, cmd: impl } = await loadMockNpm(t, { 24 command: cmd, 25 config: { usage: true }, 26 }) 27 const ctor = impl.constructor 28 29 t.notOk(impl.completion, 'completion is static, not on instance') 30 if (ctor.completion) { 31 t.ok(isAsyncFn(ctor.completion), 'completion is async function') 32 counts.completion++ 33 } 34 35 // exec fn 36 t.ok(isAsyncFn(impl.exec), 'exec is async') 37 t.ok(impl.exec.length <= 1, 'exec fn has 0 or 1 args') 38 39 // workspaces 40 t.type(ctor.ignoreImplicitWorkspace, 'boolean', 'ctor has ignoreImplictWorkspace boolean') 41 if (ctor.ignoreImplicitWorkspace !== BaseCommand.ignoreImplicitWorkspace) { 42 counts.ignoreImplicitWorkspace++ 43 } 44 45 t.type(ctor.workspaces, 'boolean', 'ctor has workspaces boolean') 46 if (ctor.workspaces !== BaseCommand.workspaces) { 47 counts.workspaces++ 48 } 49 50 if (ctor.workspaces) { 51 t.ok(isAsyncFn(impl.execWorkspaces), 'execWorkspaces is async') 52 t.ok(impl.exec.length <= 1, 'execWorkspaces fn has 0 or 1 args') 53 } else { 54 t.notOk(impl.execWorkspaces, 'has no execWorkspaces fn') 55 } 56 57 // name/desc 58 t.ok(impl.description, 'implementation has a description') 59 t.equal(impl.description, ctor.description, 'description is same on instance and ctor') 60 t.ok(impl.name, 'implementation has a name') 61 t.equal(impl.name, ctor.name, 'name is same on instance and ctor') 62 t.equal(cmd, impl.name, 'command list and name are the same') 63 64 // params are optional 65 if (impl.params) { 66 t.equal(impl.params, ctor.params, 'params is same on instance and ctor') 67 t.ok(impl.params, 'implementation has a params') 68 } else { 69 counts.noParams++ 70 } 71 72 // usage 73 t.match(impl.usage, cmd, 'usage contains the command') 74 await npm.exec(cmd, []) 75 t.match(outputs[0][0], impl.usage, 'usage is what is output') 76 t.match(outputs[0][0], ctor.describeUsage, 'usage is what is output') 77 t.notOk(impl.describeUsage, 'describe usage is only static') 78 }) 79 } 80 81 // make sure refactors dont move or rename these static properties since 82 // we guard against the tests for them above 83 t.ok(counts.completion > 0, 'has some completion functions') 84 t.ok(counts.ignoreImplicitWorkspace > 0, 'has some commands that change ignoreImplicitWorkspace') 85 t.ok(counts.workspaces > 0, 'has some commands that change workspaces') 86 t.ok(counts.noParams > 0, 'has some commands that do not have params') 87}) 88