diff options
author | Gar <gar+gh@danger.computer> | 2021-06-24 20:04:57 +0300 |
---|---|---|
committer | Gar <gar+gh@danger.computer> | 2021-07-29 19:16:31 +0300 |
commit | 6a8086e258aa209b877e182db4b75f11de5b291d (patch) | |
tree | d24d982c63ba72e34acc32ff9b7ba99d9e6885d4 /test | |
parent | e7e1181702db2cf5d90b0c651ed5372b7ad13c74 (diff) |
fix(tests): move more tests to use real npm
This moves a handful of the smaller tests to using the new npm mock that
uses the real actual npm object. It also extends the testing surface
area of a few tests back down into the actual `process.spawn` that
results, instead of anything internal to the code.
Some dead code in `lib/test.js` was found during this, as well as an
instance of a module throwing a string instead of an error object.
PR-URL: https://github.com/npm/cli/pull/3463
Credit: @wraithgar
Close: #3463
Reviewed-by: @nlf
Diffstat (limited to 'test')
-rw-r--r-- | test/fixtures/mock-npm.js | 9 | ||||
-rw-r--r-- | test/lib/find-dupes.js | 36 | ||||
-rw-r--r-- | test/lib/get.js | 26 | ||||
-rw-r--r-- | test/lib/load-all.js | 22 | ||||
-rw-r--r-- | test/lib/prefix.js | 26 | ||||
-rw-r--r-- | test/lib/prune.js | 20 | ||||
-rw-r--r-- | test/lib/restart.js | 48 | ||||
-rw-r--r-- | test/lib/root.js | 26 | ||||
-rw-r--r-- | test/lib/set.js | 27 | ||||
-rw-r--r-- | test/lib/start.js | 48 | ||||
-rw-r--r-- | test/lib/stop.js | 48 | ||||
-rw-r--r-- | test/lib/test.js | 60 | ||||
-rw-r--r-- | test/lib/whoami.js | 50 |
13 files changed, 253 insertions, 193 deletions
diff --git a/test/fixtures/mock-npm.js b/test/fixtures/mock-npm.js index e3be10b4b..3faf8d5cf 100644 --- a/test/fixtures/mock-npm.js +++ b/test/fixtures/mock-npm.js @@ -9,6 +9,10 @@ for (const level in npmlog.levels) const { title, execPath } = process const RealMockNpm = (t, otherMocks = {}) => { + t.afterEach(() => { + outputs.length = 0 + logs.length = 0 + }) t.teardown(() => { npm.perfStop() npmlog.record.length = 0 @@ -22,6 +26,9 @@ const RealMockNpm = (t, otherMocks = {}) => { }) const logs = [] const outputs = [] + const joinedOutput = () => { + return outputs.map(o => o.join(' ')).join('\n') + } const npm = t.mock('../../lib/npm.js', otherMocks) const command = async (command, args = []) => { return new Promise((resolve, reject) => { @@ -43,7 +50,7 @@ const RealMockNpm = (t, otherMocks = {}) => { } } npm.output = (...msg) => outputs.push(msg) - return { npm, logs, outputs, command } + return { npm, logs, outputs, command, joinedOutput } } const realConfig = require('../../lib/utils/config') diff --git a/test/lib/find-dupes.js b/test/lib/find-dupes.js index c7b33ceb6..17940764b 100644 --- a/test/lib/find-dupes.js +++ b/test/lib/find-dupes.js @@ -1,24 +1,26 @@ const t = require('tap') -const FindDupes = require('../../lib/find-dupes.js') +const { real: mockNpm } = require('../fixtures/mock-npm') -t.test('should run dedupe in dryRun mode', (t) => { - t.plan(3) - const findDupesTest = new FindDupes({ - config: { - set: (k, v) => { - t.match(k, 'dry-run') - t.match(v, true) - }, +t.test('should run dedupe in dryRun mode', async (t) => { + t.plan(5) + const { npm, command } = mockNpm(t, { + '@npmcli/arborist': function (args) { + t.ok(args, 'gets options object') + t.ok(args.path, 'gets path option') + t.ok(args.dryRun, 'is called in dryRun mode') + this.dedupe = () => { + t.ok(true, 'dedupe is called') + } }, - commands: { - dedupe: (args, cb) => { - t.match(args, []) - cb() - }, + '../../lib/utils/reify-finish.js': (npm, arb) => { + t.ok(arb, 'gets arborist tree') }, }) - findDupesTest.exec({}, () => { - t.end() - }) + await npm.load() + // explicitly set to false so we can be 100% sure it's always true when it + // hits arborist + npm.config.set('dry-run', false) + npm.config.set('prefix', 'foo') + await command('find-dupes') }) diff --git a/test/lib/get.js b/test/lib/get.js index 9b77fbba3..30e26b745 100644 --- a/test/lib/get.js +++ b/test/lib/get.js @@ -1,16 +1,16 @@ const t = require('tap') +const { real: mockNpm } = require('../fixtures/mock-npm') -t.test('should retrieve values from npm.commands.config', (t) => { - const Get = t.mock('../../lib/get.js') - const get = new Get({ - commands: { - config: ([action, arg]) => { - t.equal(action, 'get', 'should use config get action') - t.equal(arg, 'foo', 'should use expected key') - t.end() - }, - }, - }) - - get.exec(['foo']) +t.test('should retrieve values from config', async t => { + const { joinedOutput, command, npm } = mockNpm(t) + const name = 'editor' + const value = 'vigor' + await npm.load() + npm.config.set(name, value) + await command('get', [name]) + t.equal( + joinedOutput(), + value, + 'outputs config item' + ) }) diff --git a/test/lib/load-all.js b/test/lib/load-all.js index e6e407805..c38c24493 100644 --- a/test/lib/load-all.js +++ b/test/lib/load-all.js @@ -1,15 +1,24 @@ const t = require('tap') const glob = require('glob') const { resolve } = require('path') +const { real: mockNpm } = require('../fixtures/mock-npm') const full = process.env.npm_lifecycle_event === 'check-coverage' if (!full) t.pass('nothing to do here, not checking for full coverage') else { - // some files do config.get() on load, so have to load npm first - const npm = require('../../lib/npm.js') - t.test('load npm first', t => npm.load(t.end)) + const { npm } = mockNpm(t) + + t.teardown(() => { + const exitHandler = require('../../lib/utils/exit-handler.js') + exitHandler.setNpm(npm) + exitHandler() + }) + + t.test('load npm first', async t => { + await npm.load() + }) t.test('load all the files', t => { // just load all the files so we measure coverage for the missing tests @@ -21,11 +30,4 @@ else { t.pass('loaded all files') t.end() }) - - t.test('call the exit handler so we dont freak out', t => { - const exitHandler = require('../../lib/utils/exit-handler.js') - exitHandler.setNpm(npm) - exitHandler() - t.end() - }) } diff --git a/test/lib/prefix.js b/test/lib/prefix.js index 526631388..18a37f3cc 100644 --- a/test/lib/prefix.js +++ b/test/lib/prefix.js @@ -1,19 +1,13 @@ const t = require('tap') +const { real: mockNpm } = require('../fixtures/mock-npm') -t.test('prefix', (t) => { - t.plan(3) - const dir = '/prefix/dir' - - const Prefix = require('../../lib/prefix.js') - const prefix = new Prefix({ - prefix: dir, - output: (output) => { - t.equal(output, dir, 'prints the correct directory') - }, - }) - - prefix.exec([], (err) => { - t.error(err, 'npm prefix') - t.ok('should have printed directory') - }) +t.test('prefix', async (t) => { + const { joinedOutput, command, npm } = mockNpm(t) + await npm.load() + await command('prefix') + t.equal( + joinedOutput(), + npm.prefix, + 'outputs npm.prefix' + ) }) diff --git a/test/lib/prune.js b/test/lib/prune.js index 87bb1370f..3e47feb46 100644 --- a/test/lib/prune.js +++ b/test/lib/prune.js @@ -1,7 +1,9 @@ const t = require('tap') +const { real: mockNpm } = require('../fixtures/mock-npm') -t.test('should prune using Arborist', (t) => { - const Prune = t.mock('../../lib/prune.js', { +t.test('should prune using Arborist', async (t) => { + t.plan(4) + const { command, npm } = mockNpm(t, { '@npmcli/arborist': function (args) { t.ok(args, 'gets options object') t.ok(args.path, 'gets path option') @@ -13,16 +15,6 @@ t.test('should prune using Arborist', (t) => { t.ok(arb, 'gets arborist tree') }, }) - const prune = new Prune({ - prefix: 'foo', - flatOptions: { - foo: 'bar', - }, - }) - prune.exec(null, er => { - if (er) - throw er - t.ok(true, 'callback is called') - t.end() - }) + await npm.load() + await command('prune') }) diff --git a/test/lib/restart.js b/test/lib/restart.js index 9719476c4..153c31447 100644 --- a/test/lib/restart.js +++ b/test/lib/restart.js @@ -1,16 +1,36 @@ const t = require('tap') -let runArgs -const npm = { - commands: { - 'run-script': (args, cb) => { - runArgs = args - cb() - }, - }, -} -const Restart = require('../../lib/restart.js') -const restart = new Restart(npm) -restart.exec(['foo'], () => { - t.match(runArgs, ['restart', 'foo']) - t.end() +const spawk = require('spawk') +const { real: mockNpm } = require('../fixtures/mock-npm') + +spawk.preventUnmatched() +t.teardown(() => { + spawk.unload() +}) + +// TODO this ... smells. npm "script-shell" config mentions defaults but those +// are handled by run-script, not npm. So for now we have to tie tests to some +// pretty specific internals of runScript +const makeSpawnArgs = require('@npmcli/run-script/lib/make-spawn-args.js') + +t.test('should run stop script from package.json', async t => { + const prefix = t.testdir({ + 'package.json': JSON.stringify({ + name: 'x', + version: '1.2.3', + scripts: { + restart: 'node ./test-restart.js', + }, + }), + }) + const { command, npm } = mockNpm(t) + await npm.load() + npm.log.level = 'silent' + npm.localPrefix = prefix + const [scriptShell] = makeSpawnArgs({ path: prefix }) + const script = spawk.spawn(scriptShell, (args) => { + t.ok(args.includes('node ./test-restart.js "foo"'), 'ran stop script with extra args') + return true + }) + await command('restart', ['foo']) + t.ok(script.called, 'script ran') }) diff --git a/test/lib/root.js b/test/lib/root.js index 5460f3d49..7b91654c6 100644 --- a/test/lib/root.js +++ b/test/lib/root.js @@ -1,19 +1,13 @@ const t = require('tap') +const { real: mockNpm } = require('../fixtures/mock-npm') -t.test('root', (t) => { - t.plan(3) - const dir = '/root/dir' - - const Root = require('../../lib/root.js') - const root = new Root({ - dir, - output: (output) => { - t.equal(output, dir, 'prints the correct directory') - }, - }) - - root.exec([], (err) => { - t.error(err, 'npm root') - t.ok('should have printed directory') - }) +t.test('prefix', async (t) => { + const { joinedOutput, command, npm } = mockNpm(t) + await npm.load() + await command('root') + t.equal( + joinedOutput(), + npm.dir, + 'outputs npm.dir' + ) }) diff --git a/test/lib/set.js b/test/lib/set.js index f51065a4b..14d094001 100644 --- a/test/lib/set.js +++ b/test/lib/set.js @@ -1,5 +1,32 @@ const t = require('tap') +// can't run this until npm set can save to npm.localPrefix +t.skip('npm set', async t => { + const { real: mockNpm } = require('../fixtures/mock-npm') + const { joinedOutput, command, npm } = mockNpm(t) + await npm.load() + + t.test('no args', async t => { + t.rejects( + command('set'), + /Usage:/, + 'prints usage' + ) + }) + + t.test('test-config-item', async t => { + npm.localPrefix = t.testdir({}) + t.not(npm.config.get('test-config-item', 'project'), 'test config value', 'config is not already new value') + // This will write to ~/.npmrc! + // Don't unskip until we can write to project level + await command('set', ['test-config-item=test config value']) + t.equal(joinedOutput(), '', 'outputs nothing') + t.equal(npm.config.get('test-config-item', 'project'), 'test config value', 'config is set to new value') + }) +}) + +// Everything after this can go away once the above test is unskipped + let configArgs = null const npm = { commands: { diff --git a/test/lib/start.js b/test/lib/start.js index 4e77f9691..5c38c71a9 100644 --- a/test/lib/start.js +++ b/test/lib/start.js @@ -1,16 +1,36 @@ const t = require('tap') -let runArgs -const npm = { - commands: { - 'run-script': (args, cb) => { - runArgs = args - cb() - }, - }, -} -const Start = require('../../lib/start.js') -const start = new Start(npm) -start.exec(['foo'], () => { - t.match(runArgs, ['start', 'foo']) - t.end() +const spawk = require('spawk') +const { real: mockNpm } = require('../fixtures/mock-npm') + +spawk.preventUnmatched() +t.teardown(() => { + spawk.unload() +}) + +// TODO this ... smells. npm "script-shell" config mentions defaults but those +// are handled by run-script, not npm. So for now we have to tie tests to some +// pretty specific internals of runScript +const makeSpawnArgs = require('@npmcli/run-script/lib/make-spawn-args.js') + +t.test('should run stop script from package.json', async t => { + const prefix = t.testdir({ + 'package.json': JSON.stringify({ + name: 'x', + version: '1.2.3', + scripts: { + start: 'node ./test-start.js', + }, + }), + }) + const { command, npm } = mockNpm(t) + await npm.load() + npm.log.level = 'silent' + npm.localPrefix = prefix + const [scriptShell] = makeSpawnArgs({ path: prefix }) + const script = spawk.spawn(scriptShell, (args) => { + t.ok(args.includes('node ./test-start.js "foo"'), 'ran start script with extra args') + return true + }) + await command('start', ['foo']) + t.ok(script.called, 'script ran') }) diff --git a/test/lib/stop.js b/test/lib/stop.js index 92ca84bd8..04cdb4e5e 100644 --- a/test/lib/stop.js +++ b/test/lib/stop.js @@ -1,16 +1,36 @@ const t = require('tap') -let runArgs -const npm = { - commands: { - 'run-script': (args, cb) => { - runArgs = args - cb() - }, - }, -} -const Stop = require('../../lib/stop.js') -const stop = new Stop(npm) -stop.exec(['foo'], () => { - t.match(runArgs, ['stop', 'foo']) - t.end() +const spawk = require('spawk') +const { real: mockNpm } = require('../fixtures/mock-npm') + +spawk.preventUnmatched() +t.teardown(() => { + spawk.unload() +}) + +// TODO this ... smells. npm "script-shell" config mentions defaults but those +// are handled by run-script, not npm. So for now we have to tie tests to some +// pretty specific internals of runScript +const makeSpawnArgs = require('@npmcli/run-script/lib/make-spawn-args.js') + +t.test('should run stop script from package.json', async t => { + const prefix = t.testdir({ + 'package.json': JSON.stringify({ + name: 'x', + version: '1.2.3', + scripts: { + stop: 'node ./test-stop.js', + }, + }), + }) + const { command, npm } = mockNpm(t) + await npm.load() + npm.log.level = 'silent' + npm.localPrefix = prefix + const [scriptShell] = makeSpawnArgs({ path: prefix }) + const script = spawk.spawn(scriptShell, (args) => { + t.ok(args.includes('node ./test-stop.js "foo"'), 'ran stop script with extra args') + return true + }) + await command('stop', ['foo']) + t.ok(script.called, 'script ran') }) diff --git a/test/lib/test.js b/test/lib/test.js index c151b1e82..d597ba274 100644 --- a/test/lib/test.js +++ b/test/lib/test.js @@ -1,38 +1,36 @@ const t = require('tap') -let RUN_ARGS = null -const npm = { - commands: { - 'run-script': (args, cb) => { - RUN_ARGS = args - cb() - }, - }, -} -const Test = require('../../lib/test.js') -const test = new Test(npm) +const spawk = require('spawk') +const { real: mockNpm } = require('../fixtures/mock-npm') -t.test('run a test', t => { - test.exec([], (er) => { - t.strictSame(RUN_ARGS, ['test'], 'added "test" to the args') - }) - test.exec(['hello', 'world'], (er) => { - t.strictSame(RUN_ARGS, ['test', 'hello', 'world'], 'added positional args') - }) +spawk.preventUnmatched() +t.teardown(() => { + spawk.unload() +}) - const lcErr = Object.assign(new Error('should not see this'), { - code: 'ELIFECYCLE', - }) - const otherErr = new Error('should see this') +// TODO this ... smells. npm "script-shell" config mentions defaults but those +// are handled by run-script, not npm. So for now we have to tie tests to some +// pretty specific internals of runScript +const makeSpawnArgs = require('@npmcli/run-script/lib/make-spawn-args.js') - npm.commands['run-script'] = (args, cb) => cb(lcErr) - test.exec([], (er) => { - t.equal(er, 'Test failed. See above for more details.') +t.test('should run stop script from package.json', async t => { + const prefix = t.testdir({ + 'package.json': JSON.stringify({ + name: 'x', + version: '1.2.3', + scripts: { + test: 'node ./test-test.js', + }, + }), }) - - npm.commands['run-script'] = (args, cb) => cb(otherErr) - test.exec([], (er) => { - t.match(er, { message: 'should see this' }) + const { command, npm } = mockNpm(t) + await npm.load() + npm.log.level = 'silent' + npm.localPrefix = prefix + const [scriptShell] = makeSpawnArgs({ path: prefix }) + const script = spawk.spawn(scriptShell, (args) => { + t.ok(args.includes('node ./test-test.js "foo"'), 'ran test script with extra args') + return true }) - - t.end() + await command('test', ['foo']) + t.ok(script.called, 'script ran') }) diff --git a/test/lib/whoami.js b/test/lib/whoami.js index 9190e3858..c54ee2a5a 100644 --- a/test/lib/whoami.js +++ b/test/lib/whoami.js @@ -1,41 +1,25 @@ const t = require('tap') -const { fake: mockNpm } = require('../fixtures/mock-npm') +const { real: mockNpm } = require('../fixtures/mock-npm') -t.test('whoami', (t) => { - t.plan(3) - const Whoami = t.mock('../../lib/whoami.js', { - '../../lib/utils/get-identity.js': () => Promise.resolve('foo'), - }) - const npm = mockNpm({ - config: { json: false }, - output: (output) => { - t.equal(output, 'foo', 'should output the username') - }, - }) - - const whoami = new Whoami(npm) +const username = 'foo' +const { joinedOutput, command, npm } = mockNpm(t, { + '../../lib/utils/get-identity.js': () => Promise.resolve(username), +}) - whoami.exec([], (err) => { - t.error(err, 'npm whoami') - t.ok('should successfully print username') - }) +t.before(async () => { + await npm.load() }) -t.test('whoami json', (t) => { - t.plan(3) - const Whoami = t.mock('../../lib/whoami.js', { - '../../lib/utils/get-identity.js': () => Promise.resolve('foo'), - }) - const npm = mockNpm({ - config: { json: true }, - output: (output) => { - t.equal(output, '"foo"', 'should output the username') - }, - }) - const whoami = new Whoami(npm) +t.test('npm whoami', async (t) => { + await command('whoami') + t.equal(joinedOutput(), username, 'should print username') +}) - whoami.exec([], (err) => { - t.error(err, 'npm whoami') - t.ok('should successfully print username as json') +t.test('npm whoami --json', async (t) => { + t.teardown(() => { + npm.config.set('json', false) }) + npm.config.set('json', true) + await command('whoami') + t.equal(JSON.parse(joinedOutput()), username, 'should print username') }) |