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